|
|
| Посл.отвђт | Сообщенiе |
|
|
Дата: Май 5, 2004 00:37:53 Собсно сабж, как бы замутит побыстрее, и без чтения/записи в памятьть, т.е. арифметическими операциями над 32х битным числом. |
|
|
Дата: Май 5, 2004 04:21:04#pragma warning(disable:4035)
__inline int _bswap(int val){
_asm{
mov eax,val
mov edx,eax
shr eax,16
rol ax,8
rol dx,8
shl edx,16
or eax,edx
}
} |
|
|
Дата: Май 5, 2004 09:28:43 · Поправил: S_T_A_S_ Хм.. а чем собсно BSWAP хуже этого ^^ ЗЫ Что-то мне кажется где-то есть какой-то подвох :) ---------------- похоже понял.. надо вариант Quantum перевести на С =) |
|
|
Дата: Май 5, 2004 13:32:40 · Поправил: Dr.Golova > похоже понял.. надо вариант Quantum перевести на С Именно, знаете, не на всех серверах стоят x86 совместимые камни :) А на асме извратиться я и сам могу: xchg al, ah rol eax, 10h xchg al, ah |
|
|
Дата: Май 5, 2004 14:08:21 Я тут подумал, и решил написать маленький тест. #include <windows.h>
/*__inline*/ DWORD _bswap1(DWORD val)
{
return LOBYTE(LOWORD(val))<<24
| HIBYTE(LOWORD(val))<<16
| LOBYTE(HIWORD(val))<<8
| HIBYTE(HIWORD(val));
}
#pragma warning(disable:4035)
/*__inline*/ DWORD _bswap2(DWORD val)
{
__asm
{
mov eax,val
bswap eax
}
}
#pragma warning(default:4035)
/*__inline*/ DWORD _bswap3(DWORD val)
{
return ((val&0xFF)<<24)
| ((val&0xFF00)<<8)
| ((val&0xFF0000)>>8)
| ((val&0xFF000000)>>24);
}
#pragma warning(disable:4035)
/*__inline*/ DWORD _bswap4(DWORD val)
{
__asm
{
mov eax,val
rol ax,8
rol eax,16
rol ax,8
}
}
#pragma warning(default:4035)
#pragma warning(disable:4035)
/*__inline*/ DWORD _bswap5(DWORD val){
_asm{
mov eax,val
mov edx,eax
shr eax,16
rol ax,8
rol dx,8
shl edx,16
or eax,edx
}
}
#pragma warning(default:4035)
DWORD a=0x12345678;
#pragma comment(linker, "/entry:main")
void main()
{
char buf1[1024];
char buf2[1024];
DWORD b1,b2,b3,b4,b5;
DWORD t1,t2,t3,t4,t5,t6;
DWORD i;
DWORD loops=0x10000000;
t1=GetTickCount();
for (i=0;i<loops;i++) b1=_bswap1(a);
t2=GetTickCount();
for (i=0;i<loops;i++) b2=_bswap2(a);
t3=GetTickCount();
for (i=0;i<loops;i++) b3=_bswap3(a);
t4=GetTickCount();
for (i=0;i<loops;i++) b4=_bswap4(a);
t5=GetTickCount();
for (i=0;i<loops;i++) b5=_bswap5(a);
t6=GetTickCount();
wsprintf(buf1,"0x%08X, 0x%08X, 0x%08X, 0x%08X, 0x%08X",b1,b2,b3,b4,b5);
wsprintf(buf2,"swap1=%i, swap2=%i, swap3=%i, swap4=%i, swap5=%i",t2-t1,t3-t2,t4-t3,t5-t4,t6-t5);
MessageBox(0,buf2,buf1,0);
ExitProcess(0);
}Инлайны специально закомментировал, чтобы оптимизатор не повыбрасывал циклы нафиг :)
И вот результат на селероне 2600 (запускал 3 раза, просто на всякий случай): swap1=1943, swap2=1292, swap3=1512, swap4=1512, swap5=1512 swap1=1942, swap2=1282, swap3=1522, swap4=1503, swap5=1522 swap1=1953, swap2=1282, swap3=1512, swap4=1522, swap5=1522Однако, похоже, что собственно bswap и есть быстрее всего :) |
|
|
Дата: Май 5, 2004 14:15:32 · Поправил: RobinFood Пока писал, появилось уточнение требований :) Тогда вставлю еще 5 копеек вдогонку. Если камень не x86, и именно на нем надо добиться оптимальной скорости, то лучше потестировать разные варианты конкретно на этом камне (и, кстати, конкретно на используемом компиляторе). Ну а если все-таки хочется кросс-платформенности, то, думаю, вполне можно брать вариант №3 - он по скорости от вариантов 4 и 5 практически не отличается (отличие в пределах погрешности измерений). |
|
|
Дата: Май 5, 2004 22:41:20 Dr.Golova И не все процессоры поддерживают xchg ah,al. Лучше уж rol ax,8, хотя и это не универсально. Я тут подумал немного и решил, что нормально реализовать bswap можно только через ror/rol (т.е. без ротации регистра никак не выйдет), но C/C++ не поддерживает такой оператор (Вы и сами, наверное, уже заметили :-) А что мешает написать ассемблерные вставки под каждую архитектуру? Ну, вроде #ifdef _486 __inline int _bswap(int val){ _asm{ mov eax,val bswap eax } } #endif #ifdef _386 __inline int _bswap(int val){ _asm{ mov eax,val xchg al,ah rol eax,10h xchg al,ah } } #endif #ifdef _big_endian_proc __inline int _bswap(int val){ // А тут может вообще не надо переворачивать, // усли оно в памяти лежит в big endian } #endif и т.д. |
|
|
Дата: Июн 9, 2004 11:12:57 Dr.Golova Если требуется это сделать на VS - то можно воспользоваться _byteswap_ulong IMHO самый быстрый метод без асма на VS C++. Вставляет инструкцию bswap. |
|
|
Дата: Июн 10, 2004 10:44:31 · Поправил: S_T_A_S_ > И вот результат на селероне 2600 (запускал 3 раза, просто на всякий случай): swap1=1943, swap2=1292, swap3=1512, swap4=1512, swap5=1512 imho - тестирование не корректно проводилось. Резон - BSWAP выполняется один такт, а остальные варианты явно больше. Вариант без ROL/ROR и с низкой зависимостью по данным. mov eax, [arg] mov ecx, 00000ff00h mov edx, 000ff0000h mov ebx, 0ff000000h and ecx, eax and edx, eax and ebx, eax and eax, 0000000ffh shl ecx, 08h shr edx, 08h shr ebx, 18h shl eax, 18h or ecx, edx or eax, ebx or eax, ecx Возможно, так бы мог и MSVC делать для (arg&0xFF)<<24|(arg&0xFF00)<<8|(arg&0xFF0000)>>8|(arg&0xFF000000)>>24 но он почему-то делает по-другому :) |
|
|
Дата: Июн 10, 2004 14:19:42 Гм... меня тогда тоже смутило то, что результаты для разных тестов слишком похожи. Но ошибок в тесте я найти не смог, и поэтому решил, что, скорее всего, причина в работе конвеера. Или нужно было в конец каждого блока вставить CPUID, чтобы конвеер сбивать? Кстати, можешь выполнить этот же тест (+ твой вариант) на своем компе и дать результаты? P.S. А почему BSWAP выполняется 1 такт? И вообще, где можно посмотреть "растактовку" команд? В helppc когда-то были расписаны времена выполнения команд, но в описании IA-32 ничего подобного нету... |
|
|
Дата: Июн 10, 2004 19:51:55 Дык.. тут с тестированием сложно. Обвязка (цикл) весит сравнимо со сравниваемым кодом, так что даже если юзать RDTSС, то результат будет слишком не точный. А GetTickCount() - не уверен что лучше. Да самое гдавное - там же CALL / RET ещё происходит к тестируемым ф-циям. IMHO надо вставлять варианты BSWAP в реальный код и смотреть уже его.. а так тест черезчур синтетический. Поэтому результаты у меня такие (AthlonXP2000+): от swap1=3938, swap2=1984, swap3=3203, swap4=2016, swap5=2359 до swap1=829, swap2=484, swap3=0, swap4=500, swap5=703 Зависят от ключей компиляции :). По поводу BSWAP - сорри, не написал, что это для Athlon так. Для ядра P6 пишут, что BSWAP весит 2 mops, не спаривается. На PIV совсем туго - latency = 7. В новых доках intel на некоторые инструкции информацию убрал (возможно, чтобы не было видно заметного отставания от P6). Но есть старые, например Order Number: 730795-001, или совсем Order Number 242816-003). Наверняка где-нибудь на intel.com до сих пор лежат :) Для AMD - 22007.pdf |
|
|
Дата: Июн 10, 2004 22:56:53 S_T_A_S_ По поводу BSWAP - сорри, не написал, что это для Athlon так. Для ядра P6 пишут, что BSWAP весит 2 mops, не спаривается. На PIV совсем туго - latency = 7. А почему ты умолчал что Throughput для bswap = 1 В реальных прогах все равно затраты на 1 такт получается. |
|
|
Дата: Июн 10, 2004 23:20:07 · Поправил: S_T_A_S_ Latency: The number of clock cycles that are required for the execution core to complete the execution of all of the µops that form a IA-32 instruction. Throughput: The number of clock cycles to wait before the issue ports are free to accept the same instruction again. Таким образом получается, что PIV может выполнять такие инструкции с частотой, равной тактовой. Но вот результат этих инструкций будет готов несколько позже, поэтому в реальных прогах всё будет сильно зависеть от зависимости по данным. Такова плата за задранную частоту - длинный конвеер. Простой пример: mov eax, [foo] bswap eax mov ecx, [eax] |
|
Powered by miniBB 1.6 © 2001-2002
Время загрузки страницы (сек.): 0.082 |