Извините, ничего не найдено.

Не расстраивайся! Лучше выпей чайку!
Регистрация
Справка
Календарь

Вернуться   forum.boolean.name > Программирование игр для компьютеров > C++

Ответ
 
Опции темы
Старый 12.02.2015, 17:27   #1
Samodelkin
Мастер
 
Регистрация: 12.01.2009
Сообщений: 979
Написано 388 полезных сообщений
(для 631 пользователей)
dword to float4 и обратно

Вопрос касается скорее инлайн ассемблера чем С/С++.
Какими инструкциями (в том числе доступными в расширениях процессора) можно максимально быстро выполнять конвертирование dword->float4 и обратно? В частности это нужно для софтварной имплементации выборки из текстуры, обсчёт на xmm регистрах и запись обратно в текстуру.
(Offline)
 
Ответить с цитированием
Старый 13.02.2015, 00:46   #2
mr.DIMAS
Дэвелопер
 
Аватар для mr.DIMAS
 
Регистрация: 26.12.2006
Адрес: Санкт-Петербург
Сообщений: 1,572
Написано 547 полезных сообщений
(для 1,540 пользователей)
Ответ: dword to float4 и обратно

То есть тебе нужно что-то типа такого:
dword( byte1, byte2, byte3, byte4 )
byte1 -> float( 0...1 )
byte2 -> float( 0...1 )
byte3 -> float( 0...1 )
byte4 -> float( 0...1 )
и обратной запаковки в dword?

опиши подробнее, а то я ничего не понял.
__________________

(Offline)
 
Ответить с цитированием
Старый 13.02.2015, 03:26   #3
Samodelkin
Мастер
 
Регистрация: 12.01.2009
Сообщений: 979
Написано 388 полезных сообщений
(для 631 пользователей)
Ответ: dword to float4 и обратно

Сообщение от mr.DIMAS Посмотреть сообщение
То есть тебе нужно что-то типа такого:
dword( byte1, byte2, byte3, byte4 )
byte1 -> float( 0...1 )
byte2 -> float( 0...1 )
byte3 -> float( 0...1 )
byte4 -> float( 0...1 )
и обратной запаковки в dword?

опиши подробнее, а то я ничего не понял.
Да именно так.

В SSE есть инструкция которая из 4 dword'ов может в 4 float'а сконвертить. Но побайтово SSE вроде не умеют работать (только с 4 байтами через инструкцию смешивания). Разделить dword на байты можно через РОН. Но копировать напрямую из РОН в xmm тоже вроде нельзя. Получается нужно загрузить в РОН, разделить на байты, снова записать в память и от туда загрузить в xmm. В целом записываться будет в кеш L1, так что не так много времени займет, но всё же лишние действия. Так или иначе сейчас у меня код написан на С++ и судя по листингу с ассемблером там очень много кода (т. к. делается через FPU), так что возможно удастся сделать быстрей.

Ну я попозже свой велосипед выложу, просто я уверен что уже есть готовые оптимальные варианты.
(Offline)
 
Ответить с цитированием
Старый 13.02.2015, 16:24   #4
mr.DIMAS
Дэвелопер
 
Аватар для mr.DIMAS
 
Регистрация: 26.12.2006
Адрес: Санкт-Петербург
Сообщений: 1,572
Написано 547 полезных сообщений
(для 1,540 пользователей)
Ответ: dword to float4 и обратно

А не проще ли забить на используемую память? Хранить сразу 4 флоата вместо dword? По памяти всего в 4 раза возрастает расход, но убирается ебля с преобразованиями.
__________________

(Offline)
 
Ответить с цитированием
Старый 13.02.2015, 16:52   #5
Mr_F_
Терабайт исходников
 
Аватар для Mr_F_
 
Регистрация: 13.09.2008
Сообщений: 3,947
Написано 2,189 полезных сообщений
(для 6,051 пользователей)
Ответ: dword to float4 и обратно

преобразование обычных регистров в XMM и обратно операция столь долгая, что может потеряться профит от самих SSE вычислений.
__________________
бложик | geom.io | твиттер | faded | демо 1 2 | роботы | лайтмаппер
(Offline)
 
Ответить с цитированием
Сообщение было полезно следующим пользователям:
mr.DIMAS (13.02.2015)
Старый 13.02.2015, 17:06   #6
Samodelkin
Мастер
 
Регистрация: 12.01.2009
Сообщений: 979
Написано 388 полезных сообщений
(для 631 пользователей)
Ответ: dword to float4 и обратно

Сообщение от mr.DIMAS Посмотреть сообщение
А не проще ли забить на используемую память? Хранить сразу 4 флоата вместо dword? По памяти всего в 4 раза возрастает расход, но убирается ебля с преобразованиями.
Технически конечно проще. Такой вариант тоже рассматривается, пока нету больших проблем с занимаемой текстурами памяти и их можно увеличить. Но это также нагрузит и шину памяти, у неё тоже есть предел, ведь кол-во данных для чтения/записи увеличится в 4 раза. Хотя я раньше проводил тесты с выборкой одной текстуры, но сейчас в проекте используется больше чем одна и нагрузка может быть в разы больше. А в современных системах именно память самый медленный элемент. Так что все усилия по simd оптимизации и многопоточности могут упереться в скорость памяти.

К слову даже на видеокартах, где память в разы быстрее, пытаются ужать и текстуры и гбуфер, там даже за счёт сжатия текстур можно сгенерировать часть данных на ходу, не читая их вообще.

Ещё, большие текстуры хуже умещаются в кеше. На самом деле с текстурами вообще такая проблема, что их выборка часто идёт хаотично, поэтому случаются кеш-промахи, здесь я буду пробовать создавать алгоритм наподобие mip-maping'а -- он не только улучшает визуализацию текстур (хотя в моём случае нужно применять и другие методы) но суть мип-мэпинга это подобрать разрешение текстуры таким образом чтобы оно совпадало с разрешением экрана, а это означает что выборка (при сканлайн-рендере) будет более упорядочена и префетчер лучше справится с кешированием данных текстуры. Однако в моём случае (речь о рейкастинге) рендер происходит вертикальными слайсами (столбцами) и это тоже негативно влияет на кеширование, как вариант можно сохранять данные текстур (и буферов) не построчно, а столбцами.

Ну короче тут много о чём можно рассуждать, но лучше подождать когда будут объективные данные с производительностью разных тестов.

Сообщение от Mr_F_
преобразование обычных регистров в XMM и обратно операция столь долгая, что может потеряться профит от самих SSE вычислений.
Ну то есть через кеш L1 это передается? Ну не то чтоб очень дорогая, например по сравнению с чтением из памяти без префетча это много быстрее. Чтение из L1 - около 3 циклов, из L2 около 20 циклов, из памяти - 200 циклов.
Сейчас я делаю инлайн целого ассемблерного блока в несколько страниц длиной, один раз загружаю в начале и записываю в конце, в целом удаётся уместить вычисления в 8 xmm регистров. В SSE есть ручное управление префетчем, так что можно и его попробовать если что.
(Offline)
 
Ответить с цитированием
Эти 2 пользователя(ей) сказали Спасибо Samodelkin за это полезное сообщение:
Mr_F_ (13.02.2015), St_AnGer (13.02.2015)
Старый 18.02.2015, 22:55   #7
Samodelkin
Мастер
 
Регистрация: 12.01.2009
Сообщений: 979
Написано 388 полезных сообщений
(для 631 пользователей)
Ответ: 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 ) );
pMem->environmentColorVec0[ 0 ] = static_cast< uint8_t >( texEnvironmentColor >> 16 ) / 255.0f;
pMem->environmentColorVec0[ 1 ] = static_cast< uint8_t >( texEnvironmentColor >> 8 ) / 255.0f;
pMem->environmentColorVec0[ 2 ] = static_cast< uint8_t >( texEnvironmentColor ) / 255.0f;
Код конечно может быть перегружен, из-за того что я вырвал его из контекста, к сожалению сейчас основная цель у меня это сам проект, поэтому я просто не успеваю подготовить какие то сравнительные тесты и т. п. что по правилам следует делать в таких ситуациях. Значит первая строка это выборка из текстуры: не нужно вдаваться в подробности выборки, главное что в переменную texEnvironmentColor сохраняется dword значение цвета. Затем в трех последующих строках делается сдвиг и урезание до одного байта чтобы получить каждое значение цвета по очереди. Тут нужно заметить что альфа мне в данном случае не нужна и я не трачусь на неё, но вообще её получают тем же образом, если она есть в текстуре четвёртым каналом. Затем происходит деление каждого значения на 255.0f. Буква f указана явно (я её вообще всегда указываю) чтобы левый операнд сначала преобразовался к float (тут идёт неявное преобразование), и затем поделился на 255, таким образом мы ограничили значение цвета в диапазоне [0.0f; 1.0f] (вроде как можно применить термин нормализация, раз это вектор). Затем мы присваиваем полученные значения вектору pMem->environmentColorVec0, который уже там выровнен должным образом и всё такое.

Вот, конечно было бы правильным посмотреть на листинг ассемблера который выходит из компилятора, чтобы посмотреть насколько он тяжёлый (или нет), но по выше описанным причинам мне надо было спешить и я просто смотрел на FPS, стал он выше или нет. Ну и по моим ощущениям показанный выше кусок довольно медленный.

Этот кусок удобно разделить на две части: разбиение одного dword на четыре dword и преобразование четырёх dword в четыре float (ну как я уже сказал альфы в данном случае нет, но в целом мы работаем с 4 компонентами). В описании к SSE я нашёл полезную инструкцию cvtdq2ps -- конвертирует 4x dword в 4x float (также есть и обратная cvtps2dq если чо). Поэтому вторую часть алгоритма я заассемблировал так:
asm( "movaps   xmm6, %1   \n\t"        
     "movaps   xmm0, %2   \n\t"
     "cvtdq2ps xmm0, xmm0 \n\t"
     "divps    xmm0, xmm6 \n\t"
     "movaps   %0,   xmm0 "

     : "=m"( pMem->environmentColorVec0 )
     : "m"( pMem->power ),
       "m"( pMem->swap ) );
В xmm6 сразу помещаем вектор из 255, достаточно один раз если не будете затирать регистр. Затем конвертируем, нормализуем и сохраняем в память. Этот участок кода однозначно работает быстрее -- я проверил.

Небольшое отступление: вот кстати есть movaps для выровненных данных, есть movups для невыровненных, и есть допустим addps у которой второй аргумент может быть прочитан из памяти, но не указано выровнен он или нет. Сразу возникает вопрос как лучше?: сначала загрузить через movaps и затем складывать или сразу пользоваться addps? (Видимо нужно смотреть мануалы к процессорам -- там должно быть написано сколько циклов занимают те или иные инструкции на разных процессорах...)

С первой же частью сложнее -- сдвиги я выполнял на регистрах общего назначения (РОН) и как мне кажется получился оверхед:
asm( "mov      eax,  %3   \n\t"
     "xor      ebx,  ebx  \n\t"
     "mov      bl,   al   \n\t"
     "mov      %0,   ebx  \n\t"
     "shr      eax,  8    \n\t"
     "mov      bl,   al   \n\t"
     "mov      %1,   ebx  \n\t"
     "shr      eax,  8    \n\t"
     "mov      bl,   al   \n\t"
     "mov      %2,   ebx  "

     : "=m"( pMem->swap[ 2 ] ),
       "=m"( pMem->swap[ 1 ] ),
       "=m"( pMem->swap[ 0 ] )
     : "r"( texEnvironmentColor )
     : "eax", "ebx" );
Этот код оказался даже чуть медленней чем у компилятора, видимо из за чрезмерной чехорды с регистрами. В общем это первое что пришло в голову и конечно надо ещё раз посмотреть на список инстуркций и найти более отпимальные варианты, а также посмотреть листинг компилятора.

Вот, ну тут я ещё буду пробовать различные варианты и впоследствии выложу (если кому-то интересно, а то может все уже знают что нужно пользоваться GPU и не создавать лишнюю энтропию в мозгах окружающих), хотя какое-то более крупномасштабное исследование с тестами и замерами я всё таки оставлю на потом, после проекта, т. к. пока необходимой скорости я частично уже добился.

Ещё есть некоторые соображения насчёт вставки ассемблера в код С/С++. В GCC используется так называемый Extended Asm, крайне неудобная и запутанная вещь. НО, как мне кажется это следствие, а не причина, потому что причина в самом факте что нужно вставлять код ассемблера в код высокоуровневого языка. Компилятор не знает что внутри вставки, а программист пишущий вставку не знает как компилятор будет компилировать код. Предугадать состояние регистрового контекста до вхождения и восстановить его при выходе очень сложно. Если включается отпимизация то практически нереально. К тому же есть всякие ограничения, например на число параметров инлайн вставки. Ящитаю что выходом из этой ситуации будет раздельная компиляция -- С++ код в своём компиляторе, ассемблер в своём (fasm или что-то подобное), затем это вместе слинковывается. В данном случае логично будет оформить асмокод ввиде функции, а в соглашении вызовов есть чёткие правила о инициализации регистров, настройке стека, "сборки мусора" при возвращении из функции и т.д. что предотвратит всякие неожиданности со стороны компилятора. К тому же писание в отдельном файле будет стимулировать создавать большие куски асмокода что минимизирует оверхед на сам вызов этой функции, и кстати наличие большого количества ёмких регистров (даже в относительно старых процессорах) даёт возможность писать по нескольку страниц кода без обращения к памяти. Ну и просто приятный синтаксис с подсветкой в IDE/редакторах созданных специально для ассемблера играет важную роль, ведь от психического состояния программиста зависит конечный результат, насколько код будет качественный.
(Offline)
 
Ответить с цитированием
Эти 3 пользователя(ей) сказали Спасибо Samodelkin за это полезное сообщение:
Igor (19.02.2015), impersonalis (19.02.2015), mr.DIMAS (19.02.2015)
Старый 18.02.2015, 23:35   #8
impersonalis
Зануда с интернетом
 
Аватар для impersonalis
 
Регистрация: 04.09.2005
Сообщений: 14,014
Написано 6,798 полезных сообщений
(для 20,935 пользователей)
Ответ: dword to float4 и обратно

чо за фигня со шрифтом?
__________________
http://nabatchikov.com
Мир нужно делать лучше и чище. Иначе, зачем мы живем? tormoz
А я растила сына на преданьях
о принцах, троллях, потайных свиданьях,
погонях, похищениях невест.
Да кто же знал, что сказка душу съест?
(Offline)
 
Ответить с цитированием
Старый 18.02.2015, 23:38   #9
Samodelkin
Мастер
 
Регистрация: 12.01.2009
Сообщений: 979
Написано 388 полезных сообщений
(для 631 пользователей)
Ответ: dword to float4 и обратно

Сообщение от impersonalis Посмотреть сообщение
чо за фигня со шрифтом?
Что именно? Некорректно отображатеся или просто не нравится?
Я поставил [font="Comic Sans MS"][color=#303030], так вроде для глаз лучше не? Просто тут такие скучные шрифты...
(Offline)
 
Ответить с цитированием
Сообщение было полезно следующим пользователям:
impersonalis (19.02.2015)
Старый 19.02.2015, 01:07   #10
Phantom
Элита
 
Аватар для Phantom
 
Регистрация: 14.06.2008
Адрес: Украина, Киев
Сообщений: 2,273
Написано 754 полезных сообщений
(для 1,833 пользователей)
Ответ: dword to float4 и обратно

Samodelkin, http://lurkmore.to/Comic_Sans
(Offline)
 
Ответить с цитированием
Сообщение было полезно следующим пользователям:
Samodelkin (19.02.2015)
Старый 19.02.2015, 01:38   #11
mr.DIMAS
Дэвелопер
 
Аватар для mr.DIMAS
 
Регистрация: 26.12.2006
Адрес: Санкт-Петербург
Сообщений: 1,572
Написано 547 полезных сообщений
(для 1,540 пользователей)
Ответ: dword to float4 и обратно

2Samodelkin тебе с таким дрочерством надо идти в эмбеддеры - писать прошивки для микропроцессоров, но это так, к слову.

С SSE лучше\удобнее работать на интринсиках, и компилятор заодно оптимизирует SSE код.

Кстати, ты так и не пользуешься оптимизацией компилятора? Ну там может -O3 -msse2 поставить и сравнить скорости велика и то что компилятор сделает?
__________________

(Offline)
 
Ответить с цитированием
Старый 19.02.2015, 02:53   #12
Samodelkin
Мастер
 
Регистрация: 12.01.2009
Сообщений: 979
Написано 388 полезных сообщений
(для 631 пользователей)
Ответ: dword to float4 и обратно

Сообщение от mr.DIMAS Посмотреть сообщение
2Samodelkin тебе с таким дрочерством надо идти в эмбеддеры - писать прошивки для микропроцессоров, но это так, к слову.

С SSE лучше\удобнее работать на интринсиках, и компилятор заодно оптимизирует SSE код.

Кстати, ты так и не пользуешься оптимизацией компилятора? Ну там может -O3 -msse2 поставить и сравнить скорости велика и то что компилятор сделает?
Не всё так плохо как может показаться -- у меня ассемблер только в двух функциях на несколько страниц кода. Эти функции являются софтварным аналогом пиксельных шейдеров. Если я их сделаю быстрыми -- вся программа станет быстрой. Это вот самый боттлнек, поэтому эти две функции стоят того чтобы над ними поработать. Больше нигде ассемблера нет.

Конечно пользоваться интринсиками или писать высокоуровнево удобней. Для какого-нибудь среднеуровневого игрового кода так и надо делать. Но критические места стоят затраченных усилий -- главное правильно сопоставить профит и затраты.

Кстати хочу сказать что ассемблер не такой уж и плохой, особенно если его знаешь, на нём можно написать быстро и компактно. Другое дело что, как я выше отметил, инлайн ассемблер это пример того как лучше не делать, я бы писал на асме отдельные объектные файлы и линковал их.

Оптимизация обфусцирует код. Очень сложно становится отлаживать что-то на уровне ассемблера, и как было показано в презенташке по DOD, компилятор может оптимизировать только 10% кода -- остальные 90% нужно делать программисту. Пока некоторые части моего велосипеда проигрывают, некоторые лучше чем отпимизированный код компилятора, но дело обстоит так исключительно из-за моих недоработок и какого-то незнания ассемблера. Это не финальная версия и я её буду улучшать. Ещё раз повторю это буквально 10-20 страниц на всю программу (где около 10к строк). Так что это никак не задрочество.

Со шрифтами не знал, сорри, попробую подобрать другие =).
(Offline)
 
Ответить с цитированием
Старый 19.02.2015, 14:06   #13
mr.DIMAS
Дэвелопер
 
Аватар для mr.DIMAS
 
Регистрация: 26.12.2006
Адрес: Санкт-Петербург
Сообщений: 1,572
Написано 547 полезных сообщений
(для 1,540 пользователей)
Ответ: dword to float4 и обратно

Очень сложно становится отлаживать что-то на уровне ассемблера,
Отлаживают Debug-билд( -O0 ). А Release-билд( -O3 -msse2 ... ) просто компилят и кидают пользователю. Уж не думаешь ли ты что компилятор поломает твою программу?
__________________

(Offline)
 
Ответить с цитированием
Старый 19.02.2015, 16:03   #14
Samodelkin
Мастер
 
Регистрация: 12.01.2009
Сообщений: 979
Написано 388 полезных сообщений
(для 631 пользователей)
Ответ: dword to float4 и обратно

Сообщение от mr.DIMAS Посмотреть сообщение
Отлаживают Debug-билд( -O0 ). А Release-билд( -O3 -msse2 ... ) просто компилят и кидают пользователю. Уж не думаешь ли ты что компилятор поломает твою программу?
Представь себе думаю, потому что это не домыслы, а факт, что в теории оптимизации есть утверждение что в любом компиляторе у оптимизатора есть ошибки. Это написано в известной Dragon Book, а к высказываниям в таких книгах нужно прислушиваться.

Нужно изначально исходить из того что нужно тестировать две версии -- обычную и оптимизированную. Это две разные программы и их обе тестируют. Часто в оптимизированной находят новые ошибки и проблемы. Также нормальная практика не создавать оптимизированные версии ради экономии времени и бюджета.
(Offline)
 
Ответить с цитированием
Старый 19.02.2015, 16:52   #15
mr.DIMAS
Дэвелопер
 
Аватар для mr.DIMAS
 
Регистрация: 26.12.2006
Адрес: Санкт-Петербург
Сообщений: 1,572
Написано 547 полезных сообщений
(для 1,540 пользователей)
Ответ: dword to float4 и обратно

А всякие сторонние библиотеки ты тоже собираешь без оптимизации, ну например булет?
__________________

(Offline)
 
Ответить с цитированием
Ответ


Опции темы

Ваши права в разделе
Вы не можете создавать темы
Вы не можете отвечать на сообщения
Вы не можете прикреплять файлы
Вы не можете редактировать сообщения

BB коды Вкл.
Смайлы Вкл.
[IMG] код Вкл.
HTML код Выкл.


Часовой пояс GMT +4, время: 03:00.


vBulletin® Version 3.6.5.
Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
Перевод: zCarot
Style crйe par Allan - vBulletin-Ressources.com