Я когда-то оптимизировал такую вещь. На AMD быстрее всего вариант Black_Mirror (только лучше использовать несколько регистров в качестве счётчиков, чтобы уменьшить число зависимостей), на Intel - аналогичный код, но с использованием setnc
Замерил так Код (Text): @1:call numlen loop @1 Мой табличный вариант из поста #6 - 15 тактов Вариант Black_Mirror из поста #13 - 12 тактов P.S. Проц - Athlon II
murder а если попробовать сделать как предлагает Maratyszcza? Код (Text): mov ecx,5 xor ebx,ebx mov edx,ecx cmp eax,10^9 sbb ecx,ebx cmp eax,10^8 sbb edx,ebx cmp eax,10^7 sbb ecx,ebx cmp eax,10^6 sbb edx,ebx cmp eax,10^5 sbb ecx,ebx cmp eax,10^4 sbb edx,ebx cmp eax,10^3 sbb ecx,ebx cmp eax,10^2 sbb edx,ebx cmp eax,10^1 lea eax,[ecx+edx] sbb eax,ebx
Вот ещё вариант. Можно как-нибудь оптимизировать? Код (Text): function numlen4(x: dword): dword;register;assembler; asm movd xmm0,eax shufps xmm0,xmm0,0 movaps xmm1,xmm0 pcmpgtd xmm0,dqword[table] pcmpgtd xmm1,dqword[table+16] movmskps ebx,xmm0 movmskps edx,xmm1 popcnt ebx,ebx popcnt edx,edx cmp eax,10 cmc adc ebx,edx lea eax,[ebx+1] end;
Забавно, самый примитивный вариант оказался почти самым быстрым. А вот для 64битного кода остаётся только табличный и вариант c cmov. Но здесь наверно победит табличный.
Вот код для AMD (K10): Код (Text): ; Clock 0 xor ebx, ebx xor ecx, ecx xor edx, edx ; Clocks 1-2 cmp eax, 10 ; Clock 1 sbb ebx, -1 ; Clock 2 cmp eax, 100 ; Clock 1 sbb ecx, -1 ; Clock 2 cmp eax, 1000 ; Clock 1 sbb edx, -1 ; Clock 2 ; Clocks 3-4 cmp eax, 10000 ; Clock 3 sbb ebx, -1 ; Clock 4 cmp eax, 100000 ; Clock 3 sbb ecx, -1 ; Clock 4 cmp eax, 1000000 ; Clock 3 sbb edx, -1 ; Clock 4 ; Clock 5-6 cmp eax, 10000000 ; Clock 5 sbb ebx, -1 ; Clock 6 cmp eax, 100000000 ; Clock 5 sbb ecx, -1 ; Clock 6 cmp eax, 1000000000 ; Clock 5 sbb edx, -1 ; Clock 6 ; Clock 7 lea eax, [ebx + ecx * 1 + 1] ; Clock 8 add eax, edx Фенома у меня нет, так что реально измерить время не могу.
Медленнее стало. А как ты по тактам расписываешь? Да и ещё не понял как несколько cmp могут исполняться на 5 такте а зависимые от них sbb на 6.
murder можно умножить число на 2^32/10^5(только нужно правильно эту константу округлить, вроде бы вверх) если edx!=0, то в eax=-1 пересылаем в ax,dx в младших 16 битах eax будет целая часть от деления на 100000, а в старших - дробная ну а дальше как у вас пересылаем eax с размножением в xmm0, сравниваем слова(не двойные) с установкой маски для целых частей константы будут 0,9,99,999(с константой 9999 сравниваем в обычных регистрах) а для дробных 2^16/10^1,..,2^16/10^4(тут уже вроде вниз нужно округлять) в общем количество команд для SSE должно в два раза уменьшиться
murder Пожалуй, последние две строчки лучше переписать так: Код (Text): ; Clock 7 add ebx, ecx mov eax, edx stc ; Clock 8 adc eax, ebx Расписываю по таблица задержек от Агнера Фога (www.agner.org/optimize). Можно ещё заглянуть в дампы Everest'а на instlatx64.freeweb.hu Все современные процессоры умеют переименовывать eflags, поэтому каждый sbb зависит только от своего cmp
Black_mirror Я тебя понял. Но не уверен с этими округлениями. Как ты можешь быть уверен, что округлять надо всегда вниз (или вверх)?
murder Первую константу нужно округлять вверх C=42950 99999*C=FFFFD7FAh 100000*C=100007FC0h а вот остальные на самом деле должны быть 2^16/10^n-1: 9*C=5E5F6h 10*C=68DBCh 99*C=40E192h 100*C=418958h 999*C=28EB5AAh 1000*C=28F5D70h 9999*C=1998FE9Ah 10000*C=1999A660h Кстати для целых частей лучше взять константы 9,99,999,9999, потому что у нас было сравнение старшей части с 0, и по нему можно не только младшую часть единицами заполнить, но и скорректировать количество цифр.
murder Если старшая часть ненулевая, то дробную часть нужно заполнить единицами, чтобы они не портили результаты сравнений на SSE: 4500000*С=2D001674C0->2DFFFFFFFF->FFFF002D вот что должно быть.