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 и обратно
А всякие сторонние библиотеки ты тоже собираешь без оптимизации, ну например булет?
|
Часовой пояс GMT +4, время: 07:50. |
vBulletin® Version 3.6.5.
Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
Перевод: zCarot