Вариант для Intel (Core 2, Nehalem): Код (Text): ; Clock 0 xor ebx, ebx xor ecx, ecx xor edx, edx ; Clocks 1-2 cmp eax, 10 setae bl cmp eax, 100 setae cl cmp eax, 1000 setae dl ; Clocks 3-4 cmp eax, 10000 setae bh cmp eax, 100000 setae ch cmp eax, 1000000 setae dh ; Clock 4 add bl, bh add cl, ch add dl, dh ; Clocks 5-6 cmp eax, 10000000 setae bh cmp eax, 100000000 setae ch cmp eax, 1000000000 setae dh ; Clock 7 add bl, bh add cl, ch add dl, dh ; Clock 8 movzx ebx, bl movzx ecx, cl movzx edx, dl ; Clock 9 add ebx, ecx add edx, 1 ; Clock 10 lea eax, [ebx + edx * 1] IACA показывает throughput 11 (как и ожидалось) и latency 12 (ибо Front-End не успевает). Реального железа опять же у меня нет.
murder забыл поди как мы с тобой на SSE2 вычисляли mod FFF00001? Там тоже нужно было знаковый бит перед сравнением инвертировать... Кста, а вариант с ЯВУ типа length(inttostr(x)) чем плох?
это скорее не вариант яву, а вариант для тех кто совсем не экономит, но если это не надо, то почему бы и нет )
Maratyszcza Всё это только замедляет работу. Сейчас заново потестил код из поста #25 - теперь он медленнее. Быстрее всего работает #13. Может быть я где-то в коде ошибся
murder До и после выполнения rdtsc нужно сериализовать конвеер, т.е. сказать процессору, чтобы он дождался, пока все команды, которые уже загружены в конвеер, выполнятся до конца. Единственная команда, которая сериализует конвеер, и которую можно вызвать из user-mode, это cpuid. К сожалению, это команда выполняется долго (сотни тактов), поэтому нужно предпринять некоторые дополнительные усилия, чтобы учесть этот оверхед. Как это сделать можно посмотреть в примере на сайте Агнера Фога. Твой код вы нынешнем виде измеряет лишь производительность Front-End'а процессора.
murder Хотя нет, учитывая, что этот кусочек выполняется миллиард раз, на конвеер можно забить. А вот саму функцию numlen стоит заинлайнить: на таких маленьких участках кода вызов функции - это значительный (и непостоянный) оверхед. И хорошо бы ещё начало цикла выровнять (не знаю, делает ли это Delphi) на 32 байта.
Black_mirror Фуф. совсем запутался. Код (Text): table: array[0..7] of word=(9 xor $8000,trunc(0.9*65536) xor $8000,99 xor $8000,trunc(0.99*65536) xor $8000,999 xor $8000,trunc(0.999*65536) xor $800,9999 xor $8000,trunc(0.9999*65536) xor $8000); Правильно? p.s. Продолжу завтра утром
Вариант для Pentium-M Код (Text): ; Clocks 0-1 cmp eax, 10 setae bl cmp eax, 100 setae cl ; Clocks 2-3 cmp eax, 1000 setae bh cmp eax, 10000 setae ch ; Clock 4 add bl, bh add cl, ch ; Clocks 5-6 cmp eax, 100000 setae bh cmp eax, 1000000 setae ch ; Clock 7 add bl, bh add cl, ch ; Clocks 8-9 cmp eax, 10000000 setae bh cmp eax, 100000000 setae ch ; Clock 10 add bl, bh add cl, ch ; Clock 11 add bl, cl cmp eax, 1000000000 ; Clock 12 setae cl movzx eax, bl ; Clock 13 inc eax movzx ecx, cl ; Clock 14 add eax, ecx Ожидается 15 тактов, реально 17.5 тактов
Работает вроде-бы правильно, но реализация IMHO не удачная. Код (Text): const table2: array[0..7] of word=(9 xor $8000, trunc(0.00009*65536) xor $8000, 99 xor $8000, trunc(0.00099*65536) xor $8000, 999 xor $8000, trunc(0.00999*65536) xor $8000, 9999 xor $8000, trunc(0.09999*65536) xor $8000); function numlen5(x: dword): dword;register;assembler; asm mov ecx,$A7C6 mul ecx sbb ecx,ecx ;вот здесь or eax,ecx ;можно mov ax,dx ;как-то xor eax,$80008000 ;оптимизировать movd xmm0,eax shufps xmm0,xmm0,0 pcmpgtw xmm0,dqword[table2] pmovmskb eax,xmm0 popcnt eax,eax shr eax,1 ;здесь cmp dx,1 ;наверно sbb ax,-2 ;тоже end;
murder Тут не реализация неудачная, тут сама идея была неудачной, из-за всех этих махинаций количество зависимостей почти в два раза выросло. В вашем изначальном варианте можно заменить movaps на еще один pshufd, а девятое сравнение делать параллельно с вычислениями на SSE, тогда количество зависимостей немного уменьшится. Любопытно еще потестировать вариант, когда таблица уже лежит в паре регистров. Код (Text): lea edx,[eax+80000000h] cmp eax,10 movd xmm0,edx mov eax,2 pshufd xmm1,xmm0,0 pshufd xmm0,xmm0,0 sbb eax,0 pcmpgtd xmm1,[table] pcmpgtd xmm0,[table+16] movmskps ecx,xmm1 movmskps edx,xmm0 popcnt ecx,ecx popcnt edx,edx add eax,ecx add eax,edx
Black_mirror Странно. Если заинлайнить функцию есть улучшение в два такта (9 против 11). А так абсолютно одинаково (11).
Заинлайнил код из постов #13 и #33. Выдаёт 8 и 9 тактов соответственно. Похоже, что проц сам умеет грамотно расставлять инструкции в процессе выполнения.
medstrax1 Про iret не знал, а теперь ещё и забыл=) Я не знаток системных инструкций. murder Смешивать целочисленные и floating-point SSE команды при работе с одними и теми же данными нехорошо. На Nehalem это вызывает дополнительную задержку в один такт. Поэтому лучше вместо shufps использовать pshufd, а вместо movaps - movdqa. Проц умеет переупорядочивать инструкции ещё с незапамятных времён (Интелы, кроме Атома, - начиная с Pentium Pro/Pentium II, АМД - вероятно, начиная с K7)
Мне понравились #2,#5,#7. Перед вызовом освободите st6-st7. После вызова st7.empty = abs(value), st0.valid = log(10;st7). Конвенция: ccall(ret), stdcall(ret 8). Для обрубки дроби: fisttp. Пример: ;ffree st6 ;ffree st7 stdcall lg64,-1,-1 shr 1 ;2^63 fisttp dword[esp] pop reg add esp,4 ;inc reg Код (Text): proc lg64; value:qword fldlg2 fild qword[esp+4] fabs fyl2x ret endp