Поставил на виртуалку Windows 10 Build 10240. Немного поюзал, запустил новый MS Edge, метро-приложения, магазин...
Убила новая морда у окошек - выглядит вырвиглазно, потеряна какая-то строгость и чёткость UI, которая была в Windows XP/7. Всё какое-то новомодное и неуклюжее. Разнобой в интерфейсах, начиная от меню Пуск, продолжаясь в Проводнике и Панели управления и кончая метро-приложеними, просто поражает. Это какая-то сборная солянка из MFC, .NET и XAML, части которой делали разные отделы внутри корпорации и в разное время, а потом всё это собрали в один билд и назвали Windows 10. Что-то сломалось внутри Microsoft, и недавние перестановки в топ-менеджменте это только подтверждают. В общем желания ставить новую Виндоуз основной ОС не возникает, по крайней мере до широкого распространения DirectX 12 в играх.
P. S. Браузер Edge действительно неплохо оптимизирован, например на старом ноутбуке с Intel Core M фильмы с video.mail.ru в нём воспроизводятся плавнее, чем в Chrome.
четверг, 23 июля 2015 г.
среда, 22 апреля 2015 г.
Dynamic BVH
Первая рабочая реализация BVH дерева для skinned моделей:
Структура дерева предрассчитывается и размещается в нескольких GPU-буферах. Меш модели скинится, и описывающие объёмы дерева рассчитываются динамически в compute shader-e под геометрию меша.
Skinned модель и её BVH
Простой линейный алгоритм, легко ложащийся в real-time на GPU. Пока что это пробная реализация, имеющая недостатки: структура дерева далека от оптимальной (до близкой к оптимальной нужно много допиливать), каждый меш в модели строит своё дерево отдельно (а нужно хотя бы одно дерево на всю модель).
Трассировка отдельного луча на CPU
Кроме рекурсивной трассировки, я набросал на CPU итеративную версию алгоритма, т. к. DX11-шейдеры не поддерживают рекурсию. Итеративная версия использует std::stack, и практически аналогична по смыслу рекурсивной. На GPU такой стэк можно реализовать в виде фиксированного массива в шейдере (SM 4.0 indexable temp) и индекса в этом массиве. Вероятно, возможны другие варианты итеративного алгоритма, это станет предметом рассмотрения в будущем.
Также у меня появились мысли, что трассировку можно разделить на два этапа: на первом мы составляем список листьев, которые пересекает луч, а на втором этапе проходим по всем найденным листьям и выполняем тест пересечения луча и треугольника. Это упростит шейдеры каждого из этапов, т. к. бранчинг в них работать не будет и не даст возможности отделить вычисления пересечения с нодами от вычислений пересечения с треугольниками.
пятница, 17 апреля 2015 г.
воскресенье, 12 апреля 2015 г.
Закон Амдала
При чтении IT ресурсов наткнулся на так называемый Закон Амдала. Он описывался ещё на заре появления многоядерных процессоров в десктопных конфигурациях, например здесь:
Учитывая, какие сложности возникают при программировании мультипоточных приложений, а также оверхед при выполнении множества потоков, доступа к памяти, синхронизацию и т. д. можно догадаться, что на практике всё масштабируется ещё хуже, чем описывает закон Амдала.
Недавно на 3Dnews появилась любопытная статья:
которая в полной мере подтверждает график, приведённый ещё в статье 2006 года. Т. е. за 10 лет лечебная пилюля так и не была найдена.
Итак, что же мы имеем?
1) Тактовая частота CPU застряла где-то в районе 4-5 ГГц;
2) Cкорость вычислений на отдельном ядре почти не растёт;
3) Дальнейшее наращивание размеров кэшей разного уровня возможно, но заранее можно предсказать, что это даст какие-то проценты производительности, не более.
4) Со стороны ПО масса проблем. Даже последние игры с поддержкой DX11, грубо говоря - однопоточны, т. к.однопоточен сам DirectX. Даже видеокодеки x265 плохо масштабируются после определённого барьера.
5) 12-ядерный процессор в десктопных конфигурациях лишён смысла.
Неудивительно теперь, почему Intel медлит с выпуском мультиядерных процессоров в обычный пользовательский сегмент. Себестоимость производства таких кристаллов обойдётся дороже, а значит и стоковая стоимость будет выше. А когда пользователи увидят результаты тестов этих процессоров, они зададутся вопросом - за что они платят, покупая новую модель? Неудивительно, что Intel бросились в энергоэффективность - уменьшают нормы техпроцесса, поют на каждом шагу про TDP, добавляют AVX, AVX2, AVX 3 и ещё Бог знает какие инструкции... А правда тут одна - развитие процессоров заходит (или уже зашло) в тупик, и трудно сказать, как будет действовать Intel (и другие), и что их ждёт в будущем.
суббота, 11 апреля 2015 г.
Fastest memcpy
В Сети полно тредов с обсуждением, можно ли обогнать memcpy из С lib. Из спортивного интереса я тоже решил поэкспериментировать с этим, зная что многие пытаются использовать SSE для копирования памяти через регистры xmm. Сделаем несколько допущений:
1) Большой кусок памяти (мегабайты).
2) Память выровненна по границе 16 байт.
3) Таргет CPU с обязательным наличием SSE2.
4) Соревнуемся с memcpy из VS 2012.
Можно попробовать написать максимально упрощённую под эти условия функцию, тогда как memcpy вынуждена проверять cpuid, производить проверку на выравнивание, на размер, чтобы выполнить оптимальный код, дописывать хвостики и т. д.
После различных попыток я в конце-концов остановился на комбинации SSE интринсиков _mm_stream_load_si128 / _mm_stream_si128 для копирования блоков памяти и _mm_prefetch(_MM_HINT_NTA) для указания процессору о том, что не нужно загрязнять кэш данными, так как они используются только короткое время. В x64 используются дополнительные регистры xmm8-xmm15, чтобы сократить кол-во блоков (итераций цикла) копирования. Тестовая программа состоит из 1000 циклов копирования кусков памяти размером 16Mb. Каждый цикл повторяется 10 раз чтобы удостовериться в правильности времени выполнения.
Результаты для процессора AMD FX-8320, release x86 и x64:
Судя по тому, что время отличается незначительно для x64, можно сделать вывод что CRT версия проигрывает из-за универсальности. Если бы не было 1000 циклов, эффект от ускорения был-бы несущественным. В x86 ускорение уже значительное, возможно Microsoft не удосужились оптимизировать runtime для архитектуры Bulldozer.
Кроме этого, я решил реализовать мультипоточную версию функции копирования и посмотреть, исполняется ли она эффективнее на многоядерном процессоре. Мультипоточная версия очень проста: она использует функции API Windows CreateThread/Semaphore, а также WaitForMultipleObjects для ожидания окончания работы множества потоков основным потоком. Это обеспечивает ясность над процессом распределения инструкций. Блок памяти делится на равные (по возможности) части и каждая часть копируется отдельным потоком.
Результаты для x64.
2 потока:
Как видно, использование самописного SSE и двух потоков в сумме даёт прирост в скорости около 40%. Дальнейшее увеличение кол-ва потоков практически не имеет смысла, т. к. прирост ни разу не линейный, а ядра становятся заняты. Уже после того, как код был написан и протестирован, выяснилось, что обычная память не допускает одновременного доступа к разным адресам, и что это умеет делать только Dual Port RAM.
Для чистоты эксперимента - x86 build, 8 потоков:
Максимальный прирост в скорости копирования - приблизительно двукратный. Да, x86 memcpy оказалась медленнее всего!
Наконец проверим загруженность ядер CPU копированием через xmm регистры.
2 потока:
4 потока:
8 потоков:
Масштабирование идеальное.
Выводы:
1) Обогнать memcpy из MSVCR (на AMD) можно.
2) В общем случае многопоточное копирование всё же имеет смысл.
3) Задействование большого кол-во ядер (> 2-4) бессмысленно, из-за того что память не допускает одновременного доступа к разным ядресам.
Update.
Результаты на Core i7-4790:
Результаты на Core i7-3770:
1) Большой кусок памяти (мегабайты).
2) Память выровненна по границе 16 байт.
3) Таргет CPU с обязательным наличием SSE2.
4) Соревнуемся с memcpy из VS 2012.
Можно попробовать написать максимально упрощённую под эти условия функцию, тогда как memcpy вынуждена проверять cpuid, производить проверку на выравнивание, на размер, чтобы выполнить оптимальный код, дописывать хвостики и т. д.
После различных попыток я в конце-концов остановился на комбинации SSE интринсиков _mm_stream_load_si128 / _mm_stream_si128 для копирования блоков памяти и _mm_prefetch(_MM_HINT_NTA) для указания процессору о том, что не нужно загрязнять кэш данными, так как они используются только короткое время. В x64 используются дополнительные регистры xmm8-xmm15, чтобы сократить кол-во блоков (итераций цикла) копирования. Тестовая программа состоит из 1000 циклов копирования кусков памяти размером 16Mb. Каждый цикл повторяется 10 раз чтобы удостовериться в правильности времени выполнения.
Результаты для процессора AMD FX-8320, release x86 и x64:
Судя по тому, что время отличается незначительно для x64, можно сделать вывод что CRT версия проигрывает из-за универсальности. Если бы не было 1000 циклов, эффект от ускорения был-бы несущественным. В x86 ускорение уже значительное, возможно Microsoft не удосужились оптимизировать runtime для архитектуры Bulldozer.
Кроме этого, я решил реализовать мультипоточную версию функции копирования и посмотреть, исполняется ли она эффективнее на многоядерном процессоре. Мультипоточная версия очень проста: она использует функции API Windows CreateThread/Semaphore, а также WaitForMultipleObjects для ожидания окончания работы множества потоков основным потоком. Это обеспечивает ясность над процессом распределения инструкций. Блок памяти делится на равные (по возможности) части и каждая часть копируется отдельным потоком.
Результаты для x64.
2 потока:
4 потока:
8 потоков:Как видно, использование самописного SSE и двух потоков в сумме даёт прирост в скорости около 40%. Дальнейшее увеличение кол-ва потоков практически не имеет смысла, т. к. прирост ни разу не линейный, а ядра становятся заняты. Уже после того, как код был написан и протестирован, выяснилось, что обычная память не допускает одновременного доступа к разным адресам, и что это умеет делать только Dual Port RAM.
Для чистоты эксперимента - x86 build, 8 потоков:
Максимальный прирост в скорости копирования - приблизительно двукратный. Да, x86 memcpy оказалась медленнее всего!
Наконец проверим загруженность ядер CPU копированием через xmm регистры.
2 потока:
4 потока:
8 потоков:
Масштабирование идеальное.
Выводы:
1) Обогнать memcpy из MSVCR (на AMD) можно.
2) В общем случае многопоточное копирование всё же имеет смысл.
3) Задействование большого кол-во ядер (> 2-4) бессмысленно, из-за того что память не допускает одновременного доступа к разным ядресам.
Update.
Результаты на Core i7-4790:
Результаты на Core i7-3770:
суббота, 21 марта 2015 г.
понедельник, 9 марта 2015 г.
XNAMath
На днях ради фана имплементил софтверный скиннинг моделей на CPU (на GPU проще). Для этого пришлось поглубже залезть в исходники XNAMath, которую использую вместо старой самописной мат. библиотеки.
Оказалось, что скиннинг можно ускорить, если не привязываться к SSE2, который для XNAMath в минимальных требованиях, а использовать дополнительные инструкции из SSE4 и относительно нового AVX. Давненько я не лазил по интринсикам :) А зря, там появилось много вкусненького! Такие функции как XMVector3Transform, XMVector3TransformNormal и XMMatrixMultiply могут быть ускорены использованием _mm_fmadd_ps (fused multiply-add) из AVX. _mm_dp_ps, который появился аж в 4(!) инкарнации SSE (AOS вместо SOA), можно использовать в функциях нормализации и поиска длины вектора. Если надо поточно конвертировать float->half и обратно, скажем, для замапленного буфера вершин, то можно использовать _mm_cvtps_ph и _mm_cvtph_ps из AVX, а не привязываться к софтверной реализации в XMConvertFloatToHalf. Также часто бывает что по ходу мат. вычислений нужно вставить/извлечь какой-нить float в/из XMVECTOR, и для этого есть удобные _mm_insert_ps/_mm_extract_ps. Наконец, добило меня то, что XMVector3Cross написана на SSE2 неоптимально, и можно обойтись только тремя шаффлами. После этого я решил дописать к XNAMath расширение с собственными функциями и предпочтительно использовать их, а саму библиотеку оставить как базу для всего остального, некритичного к производительности.
Ссылки:
DirectXMath: SSE4.1 and SSE4.2
DirectXMath: F16C and FMA
DirectXMath: AVX
Vector Cross Product using SSE Code
P.S.
Из забавного оказалось, что AMD подогнали собственный набор команд SSE вроде multiply-accumulate и horizontal add/sub, которые, конечно, опять никто не будет использовать (как было с 3DNow!) Update : похоже, эти инструкции будут удалены из архитектуры Zen, как бесперспективные.
Оказалось, что скиннинг можно ускорить, если не привязываться к SSE2, который для XNAMath в минимальных требованиях, а использовать дополнительные инструкции из SSE4 и относительно нового AVX. Давненько я не лазил по интринсикам :) А зря, там появилось много вкусненького! Такие функции как XMVector3Transform, XMVector3TransformNormal и XMMatrixMultiply могут быть ускорены использованием _mm_fmadd_ps (fused multiply-add) из AVX. _mm_dp_ps, который появился аж в 4(!) инкарнации SSE (AOS вместо SOA), можно использовать в функциях нормализации и поиска длины вектора. Если надо поточно конвертировать float->half и обратно, скажем, для замапленного буфера вершин, то можно использовать _mm_cvtps_ph и _mm_cvtph_ps из AVX, а не привязываться к софтверной реализации в XMConvertFloatToHalf. Также часто бывает что по ходу мат. вычислений нужно вставить/извлечь какой-нить float в/из XMVECTOR, и для этого есть удобные _mm_insert_ps/_mm_extract_ps. Наконец, добило меня то, что XMVector3Cross написана на SSE2 неоптимально, и можно обойтись только тремя шаффлами. После этого я решил дописать к XNAMath расширение с собственными функциями и предпочтительно использовать их, а саму библиотеку оставить как базу для всего остального, некритичного к производительности.
Ссылки:
DirectXMath: SSE4.1 and SSE4.2
DirectXMath: F16C and FMA
DirectXMath: AVX
Vector Cross Product using SSE Code
P.S.
Из забавного оказалось, что AMD подогнали собственный набор команд SSE вроде multiply-accumulate и horizontal add/sub, которые, конечно, опять никто не будет использовать (как было с 3DNow!) Update : похоже, эти инструкции будут удалены из архитектуры Zen, как бесперспективные.
Подписаться на:
Сообщения (Atom)


















