dword to float4 и обратно
Вопрос касается скорее инлайн ассемблера чем С/С++.
Какими инструкциями (в том числе доступными в расширениях процессора) можно максимально быстро выполнять конвертирование dword->float4 и обратно? В частности это нужно для софтварной имплементации выборки из текстуры, обсчёт на xmm регистрах и запись обратно в текстуру. |
Ответ: dword to float4 и обратно
То есть тебе нужно что-то типа такого:
Код:
dword( byte1, byte2, byte3, byte4 ) опиши подробнее, а то я ничего не понял. |
Ответ: dword to float4 и обратно
Цитата:
В SSE есть инструкция которая из 4 dword'ов может в 4 float'а сконвертить. Но побайтово SSE вроде не умеют работать (только с 4 байтами через инструкцию смешивания). Разделить dword на байты можно через РОН. Но копировать напрямую из РОН в xmm тоже вроде нельзя. Получается нужно загрузить в РОН, разделить на байты, снова записать в память и от туда загрузить в xmm. В целом записываться будет в кеш L1, так что не так много времени займет, но всё же лишние действия. Так или иначе сейчас у меня код написан на С++ и судя по листингу с ассемблером там очень много кода (т. к. делается через FPU), так что возможно удастся сделать быстрей. Ну я попозже свой велосипед выложу, просто я уверен что уже есть готовые оптимальные варианты. |
Ответ: dword to float4 и обратно
А не проще ли забить на используемую память? Хранить сразу 4 флоата вместо dword? По памяти всего в 4 раза возрастает расход, но убирается ебля с преобразованиями.
|
Ответ: dword to float4 и обратно
преобразование обычных регистров в XMM и обратно операция столь долгая, что может потеряться профит от самих SSE вычислений.
|
Ответ: dword to float4 и обратно
Цитата:
К слову даже на видеокартах, где память в разы быстрее, пытаются ужать и текстуры и гбуфер, там даже за счёт сжатия текстур можно сгенерировать часть данных на ходу, не читая их вообще. Ещё, большие текстуры хуже умещаются в кеше. На самом деле с текстурами вообще такая проблема, что их выборка часто идёт хаотично, поэтому случаются кеш-промахи, здесь я буду пробовать создавать алгоритм наподобие mip-maping'а -- он не только улучшает визуализацию текстур (хотя в моём случае нужно применять и другие методы) но суть мип-мэпинга это подобрать разрешение текстуры таким образом чтобы оно совпадало с разрешением экрана, а это означает что выборка (при сканлайн-рендере) будет более упорядочена и префетчер лучше справится с кешированием данных текстуры. Однако в моём случае (речь о рейкастинге) рендер происходит вертикальными слайсами (столбцами) и это тоже негативно влияет на кеширование, как вариант можно сохранять данные текстур (и буферов) не построчно, а столбцами. Ну короче тут много о чём можно рассуждать, но лучше подождать когда будут объективные данные с производительностью разных тестов. Цитата:
Сейчас я делаю инлайн целого ассемблерного блока в несколько страниц длиной, один раз загружаю в начале и записываю в конце, в целом удаётся уместить вычисления в 8 xmm регистров. В SSE есть ручное управление префетчем, так что можно и его попробовать если что. |
Ответ: dword to float4 и обратно
Итак, первую версию велосипеда я сделал. Надо сказать что SIMD оптимизацию я представлял более оптимистично. Результат конечно есть, но это не разы, это где-то +30%, и это требует значительного пересмотра логики кода и структуры данных. Для сравнения DOD (Data Oriented Design) подход дал мне больше скорости чем ассемблирование (при этом он делает код более простым и понятным, в отличие от ассемблирования). Так или иначе это первая версия кодов, а у меня особого опыта ассемблирования тоже пока нет, возможно дальнейшие улучшения дадут больше результата. Ну и я думаю что поэтапное представление модернизации кодов будет интересно, чтобы наглядно увидеть какие проблемы пришлось решать.
Значит сейчас пойдет речь как раз о извлечении dword значения цвета в формате ARGB и преобразовании его в 4D вектор в формате float4( R, G, B, A ). Вот исходный материал: Код:
texEnvironmentColor = cpTextureArray[ 0 ]->TexNearFast( { cWallTexCoordVecU[ 0 ], cWallTexCoordVecV[ 0 ] }, cpTextureArray[ 0 ]->ComputeLvl( cRayLenArray[ 0 ], cTwoTanAlpha, cFrameBufferSizeX, cWallWidth ) ); Вот, конечно было бы правильным посмотреть на листинг ассемблера который выходит из компилятора, чтобы посмотреть насколько он тяжёлый (или нет), но по выше описанным причинам мне надо было спешить и я просто смотрел на FPS, стал он выше или нет. Ну и по моим ощущениям показанный выше кусок довольно медленный. Этот кусок удобно разделить на две части: разбиение одного dword на четыре dword и преобразование четырёх dword в четыре float (ну как я уже сказал альфы в данном случае нет, но в целом мы работаем с 4 компонентами). В описании к SSE я нашёл полезную инструкцию cvtdq2ps -- конвертирует 4x dword в 4x float (также есть и обратная cvtps2dq если чо). Поэтому вторую часть алгоритма я заассемблировал так: Код:
asm( "movaps xmm6, %1 \n\t" Небольшое отступление: вот кстати есть movaps для выровненных данных, есть movups для невыровненных, и есть допустим addps у которой второй аргумент может быть прочитан из памяти, но не указано выровнен он или нет. Сразу возникает вопрос как лучше?: сначала загрузить через movaps и затем складывать или сразу пользоваться addps? (Видимо нужно смотреть мануалы к процессорам -- там должно быть написано сколько циклов занимают те или иные инструкции на разных процессорах...) С первой же частью сложнее -- сдвиги я выполнял на регистрах общего назначения (РОН) и как мне кажется получился оверхед: Код:
asm( "mov eax, %3 \n\t" Вот, ну тут я ещё буду пробовать различные варианты и впоследствии выложу (если кому-то интересно, а то может все уже знают что нужно пользоваться GPU и не создавать лишнюю энтропию в мозгах окружающих), хотя какое-то более крупномасштабное исследование с тестами и замерами я всё таки оставлю на потом, после проекта, т. к. пока необходимой скорости я частично уже добился. Ещё есть некоторые соображения насчёт вставки ассемблера в код С/С++. В GCC используется так называемый Extended Asm, крайне неудобная и запутанная вещь. НО, как мне кажется это следствие, а не причина, потому что причина в самом факте что нужно вставлять код ассемблера в код высокоуровневого языка. Компилятор не знает что внутри вставки, а программист пишущий вставку не знает как компилятор будет компилировать код. Предугадать состояние регистрового контекста до вхождения и восстановить его при выходе очень сложно. Если включается отпимизация то практически нереально. К тому же есть всякие ограничения, например на число параметров инлайн вставки. Ящитаю что выходом из этой ситуации будет раздельная компиляция -- С++ код в своём компиляторе, ассемблер в своём (fasm или что-то подобное), затем это вместе слинковывается. В данном случае логично будет оформить асмокод ввиде функции, а в соглашении вызовов есть чёткие правила о инициализации регистров, настройке стека, "сборки мусора" при возвращении из функции и т.д. что предотвратит всякие неожиданности со стороны компилятора. К тому же писание в отдельном файле будет стимулировать создавать большие куски асмокода что минимизирует оверхед на сам вызов этой функции, и кстати наличие большого количества ёмких регистров (даже в относительно старых процессорах) даёт возможность писать по нескольку страниц кода без обращения к памяти. Ну и просто приятный синтаксис с подсветкой в IDE/редакторах созданных специально для ассемблера играет важную роль, ведь от психического состояния программиста зависит конечный результат, насколько код будет качественный. |
Ответ: dword to float4 и обратно
чо за фигня со шрифтом?
|
Ответ: dword to float4 и обратно
Цитата:
Я поставил [font="Comic Sans MS"][color=#303030], так вроде для глаз лучше не? Просто тут такие скучные шрифты... |
Ответ: dword to float4 и обратно
Samodelkin, http://lurkmore.to/Comic_Sans
|
Ответ: dword to float4 и обратно
2Samodelkin тебе с таким дрочерством надо идти в эмбеддеры - писать прошивки для микропроцессоров, но это так, к слову.
С SSE лучше\удобнее работать на интринсиках, и компилятор заодно оптимизирует SSE код. Кстати, ты так и не пользуешься оптимизацией компилятора? Ну там может -O3 -msse2 поставить и сравнить скорости велика и то что компилятор сделает? |
Ответ: dword to float4 и обратно
Цитата:
Конечно пользоваться интринсиками или писать высокоуровнево удобней. Для какого-нибудь среднеуровневого игрового кода так и надо делать. Но критические места стоят затраченных усилий -- главное правильно сопоставить профит и затраты. Кстати хочу сказать что ассемблер не такой уж и плохой, особенно если его знаешь, на нём можно написать быстро и компактно. Другое дело что, как я выше отметил, инлайн ассемблер это пример того как лучше не делать, я бы писал на асме отдельные объектные файлы и линковал их. Оптимизация обфусцирует код. Очень сложно становится отлаживать что-то на уровне ассемблера, и как было показано в презенташке по DOD, компилятор может оптимизировать только 10% кода -- остальные 90% нужно делать программисту. Пока некоторые части моего велосипеда проигрывают, некоторые лучше чем отпимизированный код компилятора, но дело обстоит так исключительно из-за моих недоработок и какого-то незнания ассемблера. Это не финальная версия и я её буду улучшать. Ещё раз повторю это буквально 10-20 страниц на всю программу (где около 10к строк). Так что это никак не задрочество. Со шрифтами не знал, сорри, попробую подобрать другие =). |
Ответ: dword to float4 и обратно
Цитата:
|
Ответ: dword to float4 и обратно
Цитата:
Нужно изначально исходить из того что нужно тестировать две версии -- обычную и оптимизированную. Это две разные программы и их обе тестируют. Часто в оптимизированной находят новые ошибки и проблемы. Также нормальная практика не создавать оптимизированные версии ради экономии времени и бюджета. |
Ответ: dword to float4 и обратно
А всякие сторонние библиотеки ты тоже собираешь без оптимизации, ну например булет?
|
Ответ: dword to float4 и обратно
Цитата:
Буллет я собирал без оптимизации (так как там вроде есть ручное ускорение), но это потому что я не нашел бинарники, и не было времени собирать другим способом. Возможно проверенных бинарников и нет, потому что у разрабов Буллета другой подход -- они не выпускают продукт для конечного пользователя, они предлагают коды для интеграции в движок или сборку которая будет конечным продуктом. То есть например Буллет в составе Ксорса имеет проверенный Буллет (если Кнайт сделал всё как надо), а вот если скачал сурсы Буллета с оффсайта то сам собирай, проверяй, тестируй -- тебе ничего не гарантируют. А вот например тот же dx9 -- ты можешь посчитать хеш-суммы их дллок за последние 5 лет -- они все одинаковые, существует только один вариант бинарников, которые хорошо оттестирован. |
Ответ: dword to float4 и обратно
Цитата:
И вообще всё это недоверие больше походит на паранойю |
Ответ: dword to float4 и обратно
Цитата:
Я же не призываю не пользоваться им вообще, я говорю что нужно проверять его работу, а проверка требует сил времени и это надо учитывать, стоит ли оно того. Допустим если буллет становится в 5 раз быстрее, то возможно в этом случае стоит, но тогда надо хорошенько проверить его работу, и главное тестировать заного если ты обновил версию Буллета, а это значит что ты впринципе не сможешь оперативно обновлять Буллет, обычно собирают одну версию на весь жизненный цикл проекта. |
Ответ: dword to float4 и обратно
Цитата:
|
Ответ: dword to float4 и обратно
|
Ответ: dword to float4 и обратно
Стрелки не нужно переводить. Я хочу услышать обоснованный ответ, зачем смотреть то что там компилятор наворотил? Наверное: "потому, что я могу" или "потому, что я хочу"
|
Ответ: dword to float4 и обратно
Цитата:
|
Ответ: dword to float4 и обратно
Соберу без оптимизации, найду баг, уберу его, запилю опять с оптимизацией. Если говорить про мою игру, то без оптимизатора она работает в 6 раз медленнее( 20 фпс против 120 )
|
Ответ: dword to float4 и обратно
Цитата:
Кстати во многих случаях я вообще без отладки обхожусь, то есть чтобы меня правильно понимали: 99% ошибок можно исправить из высокоуровневого языка и это даже продуктивней чем пошагово что-то отлаживать или тем более ассемблировать. Но это не значит что нужно полностью исключать такую возможность. В 1% потребуется основательная отладка: я уже (вроде даже тебе) рассказывал про Betrayer -- там глюк с видеодрайверами который не могут исправить почти год пруфлинк, просто потому что для них UE3 это чёрный ящик, из-за этого у них огромные проблемы со всей их затеей с этой игрой. При этом 10 лет опыта в индустрии. Вот как так можно? Понадеялись на UE3? Я лучше заранее к таким ситуациям буду готов. Это кстати также касается того что я всегда рекомендую либо писать свой движок либо хорошенько разбирать устройство чужого движка, поначалу кажется что очень непродуктивно, нету быстрых прототипов и всё такое, зато много времени экономится в конце, когда код полностью котролируется, баги быстро правятся и вообще у всех хорошее настроение :) Ну то что у тебя 20 против 120 говорит лишь о том насколько плохой у тебя код. Если бы код был хороший то и оптимизировать было бы нечего. Ведь с этим утверждением нельзя поспорить. Когда есть опыт то хороший код пишется сразу, быстрей, чем написать плохой, получить баги при оптимизации и потратить на их исправление. Кстати работая над гбуфером ты использовал совсем другую философию, прям за каждый байт воевал, у шейдеров же тоже есть оптимизация. Можно было бы и незаморачиваться, само бы как нибудь заоптимизировалось. Ещё кто там кол-во инструкций считал чтобы глубину побыстрее восстановить. А кстати есть же ещё clang там куда более продвинутые оптимизаторы, вот его тебе надо ставить обязательно. |
Ответ: dword to float4 и обратно
ох лол, плохой код... а ниче что я использую 4 сторонних библиотеки( из которых в рантайме только булет ) в которых и просядает вся производительность без оптимизации? я ж выше писал что булет тормозное говно без оптимизации, профайлер ставит мой код в конце списка потенциально медленных мест. кароч, твои аргументы шаткие и нет оснований тратить время, когда можно поменять ключ компилятора и получить профит здесь и сейчас и не ебать себе и окружающим мозги.
|
Ответ: dword to float4 и обратно
Я ещё раз говорю что я не отказываюсь от оптимизации, я ей пользуюсь как любым другим инструментом.
Мои аргументы вполне понятно расписаны, где, зачем и когда надо отключать оптимизацию. С вашей стороны единственный аргумент это халява, а компилятор сам всё сделает, так что даже проверять результат нет причины. Зачем тогда кодить на С++? Mr_F_ уже давно на юнити и шарпе. |
Ответ: dword to float4 и обратно
Кто как хочет так и дрочит.
|
Часовой пояс GMT +4, время: 11:36. |
vBulletin® Version 3.6.5.
Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
Перевод: zCarot