Задать вопрос
  • Какой принцип в Ray Trasing рендеринге, как работает подробно?

    Миллион лучей - это очень мало.
    Отражение, преломление, рассеивание - это самая затратная операция как раз, тк многократно увеличивает пролёт луча и рандомно изменяет его путь.
    При нехватке лучей - будет очень много шумов либо слишком тёмная картинка.
    Ответ написан
    Комментировать
  • Как определить в какую сторону повернуты нормали в треугольнке, Внутрь или снаружу?

    ProgrammerForever
    @ProgrammerForever
    Учитель, автоэлектрик, программист, музыкант
    Скалярное произведение вектора нормали и вектора из противостоящей вершины до точки откуда идёт вектор нормали.
    A-I-B
    B-J-C
    C-K-A
    
    a*CI
    b*AJ
    c*BK

    Положительно = наружу
    Ответ написан
    Комментировать
  • Как транспонировать биты числа максимально быстро?

    mayton2019
    @mayton2019 Куратор тега Java
    Bigdata Engineer
    Ищется функция вида

    def transpose(matrix uint16) : uint16 = {}

    Если она ДЕТЕРМИНИРОВАНА тоесть зависит от аргумента и все. То можно ее ускорить
    путем МЕМОИЗАЦИИ тоесть создания просто таблицы расчитанных значений.
    Это будет очень быстро.

    Это удобно поскольку таблица получается не очень большая и влезает в разумные
    рамки памяти. Если допустим мы говорим о 32х битах то можно изучать варианты.
    В реальном мире линейного распределения аргументов не бывает. Распределение
    всегда косит в какую-то сторону, и этим можно вользоваться, создавая кеши значений
    типа LRU или дисковые базы данных как делают например, для
    тяжелых расчетов или в вебе для медленных источников данных.
    Ответ написан
    Комментировать
  • Как транспонировать биты числа максимально быстро?

    wataru
    @wataru Куратор тега Алгоритмы
    Разработчик на С++, экс-олимпиадник.
    Занумеруйте все биты от 0 до 16. Потом транспонируйте эту матрицу. Потом посмотрите на разность результата и конца. Вот эти вот числа - это на сколько бит надо сдвинуть исходные биты, чтобы они встали на нужное место.
    0 1 2 3
    4 5 6 7
    8 9 10 11
    12 13 14 15
    
    0 4 8 12
    1 5 9 13
    2 6 10 13
    3 7 11 15
    
    0 -3 -6 -9
    3 0 -3 -6
    ...


    Все биты с одинаковым смещением можно подсчитать за 3 операции: Сдвиг, битовая маска и побитовое или в ответ.
    Я тут вижу 7 разных чисел -9,-6,-3,0,3,6,9.
    Например, для 0 и 3 у вас будет
    answer = (source & 0x1248) | ((source << 3) | 0x2480) | ...


    Это не 8 пока еще операций, а аж 20. Возможно можно как-то еще их сгруппировать.

    Edit: Возможно, еще подход с таблицей будет быстрее. Для каждого из 16 возможных значений строк выдавайте битовое число - столбец, где эти 4 бита на позициях 0, 4, 8,12. Тогда ответ будет table[source&0xf0] | (table[(source>>4)&0xf] << 1) | (table[(source>>8)&0xf] << 2) | (table[(source>>12)&0xf] << 3).

    Тут 13 битовых операций и 4 чтения из памяти.
    Ответ написан
    2 комментария
  • Почему экраны имеют разрешения не кратны степени 2? Почему 1080, а 1024, если вместо умножения на 1080, можно сдвинуть 10 байт?

    @TomsEkb
    Дело в количестве делителей.
    Если у вас есть изображение со стороной 1024, и вам нужно его уменьшить в 3 или 5 раз - поздравляю, у вас проблемы. Всё потому, что число 1024 имеет разложение 2^10 и всего 11 делителей: 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024. Из них полезны нам от силы 4-5.
    Если же у вас сторона изображения равна 1080, то у вас большой простор для манипуляций, т.к. 1080 имеет разложение 2^3 х 3^3 х 5, а это уже целых 32 делителя: 1, 2, 3, 4, 5, 6, 8, 9, 10, 12, 15, 18, 20, 24, 27, 30, 36, 40, 45, 54, 60, 72, 90, 108, 120, 135, 180, 216, 270, 360, 540, 1080. Из них полезны уже гораздо больше, порядка 12-15 делителей. Большим достоинством является гораздо более высокая равномерность шкалы масштабирования, мы можем уменьшить изображение в 2, 3, 4, 5, 6 раз. Мы даже можем уменьшить изображение в 1.2, 1.5, 1.8 раз - и все ещё получим целые числа: 900, 720, 600. Мы можем даже поделить нацело на 1.08 и 1.35. Число 1024 здесь очень сильно проигрывает в свободе действий.
    Математика вездесуща. Всем добра.
    Ответ написан
    Комментировать
  • Почему экраны имеют разрешения не кратны степени 2? Почему 1080, а 1024, если вместо умножения на 1080, можно сдвинуть 10 байт?

    Zettabyte
    @Zettabyte
    Проф. восстановление данных ▪ Вопрос? См. профиль
    Типа так сложно сделать 76 лишних пикселей?

    Производители дисплеев не производят отдельные дисплеи, это невыгодно.
    Вместо этого они оперируют понятием mother glass:
    mother-glass.jpg
    И уже эта стеклянная подложка нарезается на отдельные экраны, которые необходимы заказчику.

    Размеры "материнского стекла" более-менее стандартизированы в зависимости от поколения. Известны их размеры и диагональ (примерно от 50 см до 5 м).

    Это позволяет в т.ч. и заказчику расчитать сколько нужных ему дисплеев поместится на одном листе и подогнать размеры так, чтобы из одной большой матрицы получить максимум готовых изделий, минимизировав "обрезки".
    Ответ написан
    Комментировать
  • Как разбить треугольник в прямоугольной области на N треугольников?

    Rsa97
    @Rsa97
    Для правильного вопроса надо знать половину ответа
    Обычная задача клипинга в 2d-графике.
    https://en.wikipedia.org/wiki/Sutherland%E2%80%93H...
    Ответ написан
    Комментировать
  • Как разбить треугольник в прямоугольной области на N треугольников?

    wataru
    @wataru Куратор тега Алгоритмы
    Разработчик на С++, экс-олимпиадник.
    mayton2019 правильно вам сказал: сначала пересеките треугольник и ваш прямоугольник. А потом триангулируйте.

    При пересечении у вас 2 выпуклых многоугольника. Алгоритм Сазерленда-Коэна тут немного перебор, ибо там один из многоугольников может быть не выпуклым. В вашем фиксированном случае скорее всего нет даже смысла заморачиваться с O(n log n) алгоритмом. Просто научитесь пересекать любые 2 отрезка. После чего пересеките каждую сторону треугольника с каждой стороной квадрата. Не надо считать за пересечение, если 2 отрезка параллельны и накладываются. Получите макимум 12 точек. К ним добавьте 3+4=7 вершины ваших многоугольников. Потом каждую точку проверьте на принадлежность обоим фигурам, и оставьте только те, которые лежат внутри или на границе. Потом постройте выпуклую оболочку из них (погуглите, эта-то задача вообще везде расписана).

    Что касается триангуляции, то у вас там получится выпуклый многоугольник. Так что можно, например, провести все хорды из одной вершины. Если же вам нужна какая-то особо "хорошая" триангуляция, то гуглите "Триангуляция Делоне". Этот алгоритм не строит очень приплюснутых треугольников, где это возможно.
    Ответ написан
    Комментировать
  • Можно ли преобразовать без копирования byte[,] в byte[]? Как избавится от пробелммы, что одной мат. библиотеке нужен [,], другой[] с width height?

    Mike_Ro
    @Mike_Ro
    Python, JS, WordPress, SEO, Bots, Adversting
    Как вариант, использовать легковесный срез массива без копирования (Span<T>):
    byte[,] data2D = ...; 
    int width = data2D.GetLength(1);
    
    // Создаем Span byte из data2D.
    Span<byte> data1D = MemoryMarshal.CreateSpan(ref data2D[0, 0], data2D.Length); 
    
    // Передаем Span byte в функцию.
    ProcessData(data1D, width);
    Ответ написан
    2 комментария
  • Как составить список уникальных комплексных решений для уравнения? Как понять что число 0.999999 то же что 1.0000001?

    @vadimr
    В общем виде, как вы её сформулировали, эта задача неразрешима. Надо исследовать саму функцию и её машинное представление. Вся наука численных методов именно об этом.
    Ответ написан
    Комментировать
  • Как составить список уникальных комплексных решений для уравнения? Как понять что число 0.999999 то же что 1.0000001?

    mayton2019
    @mayton2019
    Bigdata Engineer
    Можно методом Монте-Карло перебирать все случайные решения на всей области определения.
    Потом - делать квантизацию (так чтобы такое значение 0.99999 равнялось такому 1.000001)
    и квантизованные пары (ключи) складывать в какую-то хеш-табличку с подсчетом.
    Триллион итераций ждать не будем. Может быть где-то через тысяч сто у нас будет
    гистограмма. И бери из нее top 10 значений. Это и будут твои 10 уникальных.
    Ответ написан
    1 комментарий
  • Как передаются регистры в трансляторах? Как происходит переход, от виртуальных регистров к машинным?

    AshBlade
    @AshBlade
    Просто хочу быть счастливым
    Как перестать поддерживать ее, при выполнении клиентского кода в ходе динамической трансляции.

    Просто иметь оптимизации, которые знают, какой регистр какое название имеет.

    Раз уж ты поставил тег .NET, то у платформы есть свой JIT компилятор, который пишется под каждую платформу (CPU) и имеет знание о ее регистрах.
    Вот пример того, как это делается в исходном коде:
    1. Каждая платформа регистрирует свои регистры и информацию о них.
    #if defined(TARGET_XARCH)
    
    #if defined(TARGET_X86)
    /*
    REGDEF(name, rnum,   mask, sname) */
    REGDEF(EAX,     0,   0x01, "eax"   )
    REGDEF(ECX,     1,   0x02, "ecx"   )
    REGDEF(EDX,     2,   0x04, "edx"   )
    REGDEF(EBX,     3,   0x08, "ebx"   )
    REGDEF(ESP,     4,   0x10, "esp"   )
    REGDEF(EBP,     5,   0x20, "ebp"   )
    REGDEF(ESI,     6,   0x40, "esi"   )
    REGDEF(EDI,     7,   0x80, "edi"   )
    REGALIAS(RAX, EAX)
    REGALIAS(RCX, ECX)
    REGALIAS(RDX, EDX)
    REGALIAS(RBX, EBX)
    REGALIAS(RSP, ESP)
    REGALIAS(RBP, EBP)
    REGALIAS(RSI, ESI)
    REGALIAS(RDI, EDI)
    
    #else // !defined(TARGET_X86)
    
    /*
    REGDEF(name, rnum,   mask, sname) */
    REGDEF(RAX,     0, 0x0001, "rax"   )
    REGDEF(RCX,     1, 0x0002, "rcx"   )
    REGDEF(RDX,     2, 0x0004, "rdx"   )
    REGDEF(RBX,     3, 0x0008, "rbx"   )
    REGDEF(RSP,     4, 0x0010, "rsp"   )
    REGDEF(RBP,     5, 0x0020, "rbp"   )
    REGDEF(RSI,     6, 0x0040, "rsi"   )
    REGDEF(RDI,     7, 0x0080, "rdi"   )
    REGDEF(R8,      8, 0x0100, "r8"    )
    REGDEF(R9,      9, 0x0200, "r9"    )
    REGDEF(R10,    10, 0x0400, "r10"   )
    REGDEF(R11,    11, 0x0800, "r11"   )
    REGDEF(R12,    12, 0x1000, "r12"   )
    REGDEF(R13,    13, 0x2000, "r13"   )
    REGDEF(R14,    14, 0x4000, "r14"   )
    REGDEF(R15,    15, 0x8000, "r15"   )
    
    REGALIAS(EAX, RAX)
    REGALIAS(ECX, RCX)
    REGALIAS(EDX, RDX)
    REGALIAS(EBX, RBX)
    REGALIAS(ESP, RSP)
    REGALIAS(EBP, RBP)
    REGALIAS(ESI, RSI)
    REGALIAS(EDI, RDI)
    
    #endif // !defined(TARGET_X86)

    2. Для каждой платформы реализуются своя пара кодогенератор/эмиттер

    // Кодогенератор
    void CodeGen::genCodeForBinary(GenTreeOp* treeNode)
    {
        GenTree* op1 = treeNode->gtGetOp1();
        GenTree* op2 = treeNode->gtGetOp2();
    
        instruction ins = genGetInsForOper(treeNode->OperGet(), targetType);
    
        regNumber op1reg = op1->isUsedFromReg() ? op1->GetRegNum() : REG_NA;
        regNumber op2reg = op2->isUsedFromReg() ? op2->GetRegNum() : REG_NA;
    
        GenTree* dst;
        GenTree* src;
    
        // This is the case of reg1 = reg1 op reg2
        // We're ready to emit the instruction without any moves
        if (op1reg == targetReg)
        {
            dst = op1;
            src = op2;
        }
        // We have reg1 = reg2 op reg1
        // In order for this operation to be correct
        // we need that op is a commutative operation so
        // we can convert it into reg1 = reg1 op reg2 and emit
        // the same code as above
        else if (op2reg == targetReg)
        {
            dst = op2;
            src = op1;
        }
        // dest, op1 and op2 registers are different:
        // reg3 = reg1 op reg2
        // We can implement this by issuing a mov:
        // reg3 = reg1
        // reg3 = reg3 op reg2
        else
        {
            var_types op1Type = op1->TypeGet();
            inst_Mov(op1Type, targetReg, op1reg, /* canSkip */ false);
            regSet.verifyRegUsed(targetReg);
            gcInfo.gcMarkRegPtrVal(targetReg, op1Type);
            dst = treeNode;
            src = op2;
        }
        // try to use an inc or dec
        if (oper == GT_ADD && !varTypeIsFloating(treeNode) && src->isContainedIntOrIImmed() && !treeNode->gtOverflowEx())
        {
            if (src->IsIntegralConst(1))
            {
                emit->emitIns_R(INS_inc, emitTypeSize(treeNode), targetReg);
                genProduceReg(treeNode);
                return;
            }
            else if (src->IsIntegralConst(-1))
            {
                emit->emitIns_R(INS_dec, emitTypeSize(treeNode), targetReg);
                genProduceReg(treeNode);
                return;
            }
        }
        regNumber r = emit->emitInsBinary(ins, emitTypeSize(treeNode), dst, src);
    }
    
    // Эммитер
    /*****************************************************************************
     *
     *  Add an instruction with two register operands.
     */
    
    void emitter::emitIns_R_R(instruction ins, emitAttr attr, regNumber reg1, regNumber reg2, insOpts instOptions)
    {
        if (IsMovInstruction(ins))
        {
            assert(!"Please use emitIns_Mov() to correctly handle move elision");
            emitIns_Mov(ins, attr, reg1, reg2, /* canSkip */ false);
        }
    
        emitAttr size = EA_SIZE(attr);
    
        assert(size <= EA_64BYTE);
        noway_assert(emitVerifyEncodable(ins, size, reg1, reg2));
    
        /* Special case: "XCHG" uses a different format */
        insFormat fmt = (ins == INS_xchg) ? IF_RRW_RRW : emitInsModeFormat(ins, IF_RRD_RRD);
    
        instrDesc* id = emitNewInstrSmall(attr);
        id->idIns(ins);
        id->idInsFmt(fmt);
        id->idReg1(reg1);
        id->idReg2(reg2);
    
        if ((instOptions & INS_OPTS_EVEX_b_MASK) != INS_OPTS_NONE)
        {
            // if EVEX.b needs to be set in this path, then it should be embedded rounding.
            assert(UseEvexEncoding());
            id->idSetEvexbContext(instOptions);
        }
    
        UNATIVE_OFFSET sz = emitInsSizeRR(id);
        id->idCodeSize(sz);
    
        dispIns(id);
        emitCurIGsize += sz;
    }


    Дальше нам остается просто получить название регистра по его числу - это мы сделали на 1 шаге при их регистрации.

    Здесь применяется условная компиляция. Но в рантайме (динамически) это тоже можно реализовать - просто кидаешь везде простые массивы и индексы.
    Ответ написан
    2 комментария
  • Как ос генерируют виртуальные адреса, по которым будет поиск, не физические? Почему программы с одних адресов начинаются?

    jcmvbkbc
    @jcmvbkbc
    "I'm here to consult you" © Dogbert
    Почему программы с одних адресов начинаются?

    Потому что часть программ просто линкуется в одни и те же адреса. Это т.н. position-dependent executable, их в принципе невозможно загрузить для исполнения по другому адресу, и до недавнего времени (по крайней мере в linux) это было поведением по умолчанию. Но вот уже лет 10 во многих дистрибутивах по умолчанию используются PIE -- position-independent executables, и если не выключен ASLR, то при каждой загрузке часть адреса выбирается случайно. Но это сделано не для улучшения поведения TLB а для безопасности, поскольку это затрудняет атаки использующие известные адреса загрузки кода.

    Вот есть Tlb, представим это как линейный массив, или c , если все адреса будут одинаковы. То Очевидно что tlb будет работать в 1% своего множества

    Нет, не очевидно. Потому что никто не делает процессоры с пропорциональным отображением всего адресного пространства на все записи TLB. Обычно индексом записи TLB являются младшие биты номера страницы. Т.е. две соседние страницы могут использовать два соседних индекса в TLB, а канал TLB на 256 записей может быть полностью использован всего-то непрерывным мегабайтом памяти в 4-КБайтных страницах.
    Ответ написан
    Комментировать
  • Как ос генерируют виртуальные адреса, по которым будет поиск, не физические? Почему программы с одних адресов начинаются?

    AshBlade
    @AshBlade
    Просто хочу быть счастливым
    Ты уже сказал про старшие 20 бит, скорее всего знаешь про сегментную организацию виртуальной памяти.
    20 бит используются в 32 битных системах, а сейчас большая часть 64 битная, но это не важно.
    Принцип следующий:
    Существует 3 таблицы:
    - PGD - Page Global Directory
    - PMD - Page Middle Directory
    - PTE - Page Table Entry

    Они иерархические, т.е. запись в PGD указывает на запись в PMD, а PMD - на PTE.
    В итоге, ты приходишь с 3 "числами" - индексы для этих таблиц и последовательно приходишь к нужной PTE.
    Но тебе нужно еще 4 число - смещение относительно полученного в PTE значения (там хранятся "начала" выделенных сегментов/интервалов памяти)
    Теперь последовательно запиши эти адреса и получишь виртуальный адрес.

    TLB в данном случае - это просто кэш, чтобы ты постоянно не ходил через этот ад указателей. Он в процессе выделения памяти не участвует.

    Как выделяется реальная память - деталь реализации, о которой знать не нужно.
    Если интересно - вот статья про память в линуксе.

    P.S. пример показан на C# - там собственная виртуальная память и GC. Поэтому показывает не то, что выделила ОС.
    Ответ написан
    Комментировать
  • Как сделать проект видимым для всех проектов в папке проектов, с автоматическим подключением?

    1. Можно поместить его в один солюшен - тогда IDE сама подскажет, если нужно импортировать
    2. Можно через файл directory.props https://learn.microsoft.com/ru-ru/visualstudio/msb... - это автоматически добавит элементы в csproj, если проекты находятся в одной папке.
    3. Можно завернуть в Nuget-пакет, как предлагает # - это не сделает добавление автоматическим, но добавлять будет их не сложно.
    Ответ написан
    1 комментарий
  • Как сохранить сбоку AssemblyBuilder? Где методы Save, DefineDynamicAssembly?

    AshBlade
    @AshBlade Куратор тега C#
    Просто хочу быть счастливым
    Дока, которую ты отправил, относится к .NET Framework.
    После перехода на .NET Core и .NET Standard эту тему перестали поддерживать.
    Работы по этому поводу сейчас ведутся и в .NET 9 должно появиться: https://github.com/dotnet/runtime/issues/92975
    Но если хочешь прямо сейчас, то вот репа с примером, как можно своими силами это сделать: https://github.com/dotnet/runtimelab/blob/feature/...
    Ответ написан
    Комментировать
  • Почему иногда не создаются автоматически свойства и команды mvc wpf?

    AshBlade
    @AshBlade Куратор тега C#
    Просто хочу быть счастливым
    Это называется IntelliSense - технология автодополнения кода.
    Иногда не срабатывает, потому что:
    - либо код неправильно написан и плагин его не понимает
    - либо индексация не закончилась
    - либо действительно нельзя ничего дополнить
    - либо ошибке в самом плагине

    По твоему вопросу возможно есть тут ответ - https://stackoverflow.com/questions/47595772/custo...
    Ответ: влияет расположение элементов в коде. по ответу все должно идти в таком порядке:
    1. Окно
    2. DataContext
    3. Твой код
    4. AttachedProperty
    Ответ написан
    Комментировать
  • Почему gpu вычисления ILGPU медленнее в 10 раз чем CPU?

    AshBlade
    @AshBlade Куратор тега C#
    Просто хочу быть счастливым
    Вот из-за этого:
    // Initialize ILGPU.
         Context context = Context.CreateDefault();
         Accelerator accelerator = context.CreateCLAccelerator(1);//context.GetPreferredDevice(preferCPU: false)  .CreateAccelerator(context);
    
         // Load the data.
         using  MemoryBuffer1D<float, Stride1D.Dense> deviceData = accelerator.Allocate1D(input);
         using MemoryBuffer1D<float, Stride1D.Dense> deviceOutput = accelerator.Allocate1D<float>(output);
    
         // load / precompile the kernel
         Action<Index1D, ArrayView<float>, ArrayView<float>> loadedKernel =
             accelerator.LoadAutoGroupedStreamKernel<Index1D, ArrayView<float>, ArrayView<float>>(Kernel);
    
    
         // finish compiling and tell the accelerator to start computing the kernel
             loadedKernel((int)deviceOutput.Length, deviceData.View, deviceOutput.View);
             accelerator.Synchronize();


    Объяснение: ты в каждом тесте постоянно создаешь новые объекты, которые необходимы для работы фреймворка. Это должно быть тяжелые объекты (много содержат, тяжело инициализируются).
    Вынеси их инициализацию из-вне метода в какой нибудь Setup метод. Раз уж ты пользуешься BenchmarkDotNet, то вот помощь с этим

    UPD: оптимизировал бенчмарк - теперь GPU быстрее
    public class SampleBenchmark
    {
        static void Kernel(Index1D i, ArrayView<float> data, ArrayView<float> output)
        {
            output[i] = data[i % data.Length];
        }
        public static IEnumerable<object[]> Arguments => new[] {new object[]{new float[1000000], new float[1000000]} };
        private float[] _outputBuffer = new float[1000000];
        private float[] _inputBuffer = new float[1000000];
        
        private Context? _context;
        private Accelerator? _accelerator;
        private Action<Index1D, ArrayView<float>, ArrayView<float>>? _loadedKernel;
        private MemoryBuffer1D<float, Stride1D.Dense>? _deviceData;
        private MemoryBuffer1D<float, Stride1D.Dense>? _deviceOutput;
        
        [GlobalSetup]
        public void Setup()
        {
            var random = new Random();
            for (var i = 0; i < _inputBuffer.Length; i++)
            {
                _inputBuffer[i] = random.NextSingle();
            }
            
            _context = Context.CreateDefault();
            _accelerator = _context.GetPreferredDevice(preferCPU: false).CreateAccelerator(_context);
            _loadedKernel = _accelerator!.LoadAutoGroupedStreamKernel<Index1D, ArrayView<float>, ArrayView<float>>(Kernel);
            _deviceData = _accelerator!.Allocate1D(_inputBuffer);
            _deviceOutput = _accelerator!.Allocate1D(_outputBuffer);
        }
    
        [GlobalCleanup]
        public void TearDown()
        {
            _context?.Dispose();
            _accelerator?.Dispose();
            _deviceData?.Dispose();
            _deviceOutput?.Dispose();
        }
        [ArgumentsSource(nameof(Arguments))]
        [Benchmark]
        public void GPUTest(float[] input, float[] output)
        {
            // finish compiling and tell the accelerator to start computing the kernel
            _loadedKernel!((int)_deviceOutput.Length, _deviceData.View, _deviceOutput.View);
            _accelerator!.Synchronize();
        }
    
        [Benchmark]
        [ArgumentsSource(nameof(Arguments))]
        public void CpuTest(float[] input, float[] output)
        {
            for (var i = 0; i < input.Length; i++)
            {
                output[i] = input[i];
            }
        } 
    }


    | Method  | input           | output          | Mean      | Error    | StdDev   |
    |-------- |---------------- |---------------- |----------:|---------:|---------:|
    | GPUTest | Single[1000000] | Single[1000000] |  61.18 us | 0.101 us | 0.095 us |
    | CpuTest | Single[1000000] | Single[1000000] | 243.54 us | 3.114 us | 2.600 us |
    Ответ написан
    2 комментария
  • Как вызвать функцию на представление с контроллера в mvc паттерне?

    yarosroman
    @yarosroman Куратор тега C#
    C# the best
    1. MessageBus как вариант.
    2. Делайте свой UserControl с таймером, который будет его обновлять как надо, а к нему уже данные привязывать необходимые.
    Ответ написан
    Комментировать
  • Как сделать шаблонный метод для мат операций с числами и Типом Vector2?

    AshBlade
    @AshBlade Куратор тега C#
    Просто хочу быть счастливым
    Интерфейсов чисел он не реализует. Есть 2 костыля:
    1. Вести словарь функций сложения
    var typeToFunc = new Dictionary<Type, AddFunc>() {{typeof(int), IntAdd}, {typeof(Vector2), Vector2Add}};
    
    var left = 123;
    var right = 14455;
    var result = Add(left, right);
    Console.WriteLine($"Результат сложения {left} и {right} = {result}");
    
    var leftVector = new Vector2(123, 55);
    var rightVector = new Vector2(55, 111);
    var resultVector = Add(leftVector, rightVector);
    Console.WriteLine($"Результат сложения {leftVector} и {rightVector} = {resultVector}");
    
    T Add<T>(T left, T right)
    {
        return ( T ) typeToFunc[typeof(T)](left, right);
    }
    
    object Vector2Add(object left, object right)
    {
        return ( Vector2 ) left + ( Vector2 ) right;
    }
    
    object IntAdd(object left, object right)
    {
        return (int) left + (int) right;
    }
    
    delegate object AddFunc(object left, object right);

    2. Создай монаду с int и Vector2
    var number = NumberOrVector2<int>.FromNumber(123);
    var newNumber = number.Add(() => 23, () => throw new InvalidOperationException("хранится число"));
    if (newNumber.TryGetNumber(out var result))
    {
        Console.WriteLine($"Получился результат: {result}");
    }
    else
    {
        Console.WriteLine($"Ошибка - хранился вектор");
    }
    
    public readonly struct NumberOrVector2<TNumber> where TNumber: unmanaged, INumber<TNumber>
    {
        private readonly TNumber _number;
        private readonly Vector2? _vector;
    
        private NumberOrVector2(TNumber number, Vector2? vector)
        {
            _number = number;
            _vector = vector;
        }
    
        public bool TryGetNumber(out TNumber number)
        {
            number = _number;
            return !_vector.HasValue;
        }
    
        public bool TryGetVector(out Vector2 vector)
        {
            vector = _vector.GetValueOrDefault();
            return _vector.HasValue;
        }
    
        public NumberOrVector2<TNumber> Add(Func<TNumber> numberAdd, Func<Vector2> vectorAdd)
        {
            if (_vector is {} vector)
            {
                return new NumberOrVector2<TNumber>(_number, vector + vectorAdd());
            }
    
            return new NumberOrVector2<TNumber>(_number + numberAdd(), null);
        }
        
        public static NumberOrVector2<TNumber> FromNumber(TNumber number)
        {
            return new NumberOrVector2<TNumber>(number, null);
        }
        
        public static NumberOrVector2<TNumber> FromVector(Vector2 vector)
        {
            return new NumberOrVector2<TNumber>(default, vector);
        }
    }
    Ответ написан
    Комментировать