|
|
| Посл.отвђт | Сообщенiе |
|
|
Дата: Сен 3, 2003 18:54:32 Продолжение темы, про оптимизацию элементарных процедур конвертирования Хочу напомнить: 1. Приветствуются оптимизации по размеру и по скорости 2. Интерфейсы к процедурам должны быть неизменными То есть лучший вариант по размеру и по скорости. А вот методики конечно можно и нужно менять ;; Варианты функций для непроверенной не BCD строки IF not CM$__string_check ;; ########################################################## ;; FUN::Str_to_Int32 ;;------------------------------ ; CONV::ASMCALL ; FORMALS:: ; pbuffer = esi type:offset buffser ; - буффер строки ASCII заканчивающийся нулём ; RET:: ; int32 = edx ; - число ; errcode = eax ; - код ошибки. Если ошибок не было errcode = 0 ; pbuffer = esi ; - указывает на последний символ, ; который был прочитан функцией. ; DPN:: ; Функция конвертирует строку ASCII в число int32. В случае ; ошибки, функция возвращает ненулевое значение в eax, которое ; равно символу, не соответствующиму ASCII цифрам. ; Таким образом, не обязательно, чтобы строка завершалась нулём. ; Но вызвавшая функция сама должна оценить ситуацию. ; ;------------------------------ ; ALG: Comment # Алгоритм Функция считывает каждый символ из строки. Преобразовывает ASCII символ в BCD (ASCII-30) и запоминает его (edx). При каждом следующем считывании она увеличивает запомненное число на 10, и прибавляет к нему новое, взятое из строки. # ;; ========================================================== Str_to_Int32 proc ;; %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% Comment # # ; USE: ; eax, edx ;; %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% xor eax,eax xor edx,edx ALIGN 16 @@: ; ------- Цикл ------------ lodsb sub al,30h jb short @F ;; Если меньше -- конец cmp al,9 ja short @F lea edx,[edx+edx*4] lea edx,[eax+edx*2] jmp short @B @@: add al,30h ret Str_to_Int32 endp ;; ########################################################## ;; ########################################################## ;; FUN::Str_to_Int64 ;;------------------------------ ; CONV::ASMCALL ; FORMALS:: ; buffer = esi type:pointer ; - указатель на строку, заканчивающююся нулём. ; RET:: ; int64 = edx:eax ; - число ; ecx - Последний символ, обработанный функцией ; esi - Указатель на последний символ, ; обработанный функцией ; DPN:: ; Функция преобразовывает строку в INT64 число. ;;------------------------------ ; ALG: Comment # Алгоритм Перевод осуществляется в два этапа. Сперва переводится в число старшая часть числа После младшая И в конце результат корректируется. Основная идея разделения перевода строки в 64 число, состоит в следующем: 1. Мы переводим строку в число, пока число не превысит крайнего значения 32-bits 2. Результат запоминается 3. Переводится оставшая (младшая) часть числа, и при этом подсчитывается количество разрядов, вошедшее в это число. 4. Основываясь на колличестве разрядов, старшая половина корректируется и суммируется с младшей частью. # ;; ================================================================== Str_to_Int64 proc ;; %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% Comment # # ; USE: ; eax,edx,ecx ;; %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% xor eax,eax xor edx,edx mov ecx,eax ;; ECX будет содержать число 10*n, ;; где n - число разрядров прошедших к анализу. inc ecx ALIGN 16 @@: ; ------- Цикл ------------ lodsb sub al,30h jb short endproc ;; Если меньше -- конец cmp al,9 ja short endproc lea edx,[edx+edx*4] lea edx,[eax+edx*2] ;; Если сумматор превысит это значение, ;; значит преобразование старшей части завершено Str_to_Int64_next:: cmp edx,19999999h ;; ffffffffh/ah = 19999999h jna @B @@: ;; Когда закончилось преобразование старшей части запоминаем её в стеке push edx xor edx,edx @@: ; ------- Цикл ------------ lodsb sub al,30h jb short @F ;; Если меньше -- конец cmp al,9 ja short @F shl ecx,1 ;; edx = edx*10+eax lea edx,[edx+edx*4] lea edx,[eax+edx*2] ;; ecx = ecx*10 (shl ecx, 1 уже была выше) lea ecx,[ecx+ecx*4] ;; Следует следить за тем, чтобы сумматор множетеля разрядности ;; не переполнился cmp ecx,1000000000 jae short above_19999999h jmp @B @@: ;; Преобразование младшей части закончено. ;; В стеке была сохранена старшая часть числа ;; Помещаем её в eax. pop eax ;; Процедура корректирования результата преобразований младшей и старшей части. Str_to_Int64_correct:: correct: ;; Контекст ;; eax - старшая часть числа ;; edx - младшая часть числа ;; ecx - множитель ;; 1. Сохраняем в стеке младшую часть. push edx mul ecx ;; После этой команды в edx:eax хранится 64-bits число ;; Теперь к нему нужно прибавить оставшуюся часть результата, ;; которая находится в стеке pop ecx ;; edx:eax = edx:eax+ecx add eax,ecx adc edx,0 ;; Последний символ возвращается в cl xor ecx,ecx mov cl, byte ptr [esi-1] ret above_19999999h: ;; Управление приходит сюда, только в том случае, если число слишком ;; большое для команд lea и поэтому нужно воспользоваться другим алгоритмом. ;; Контекст: ;; edx - содержит текущее число, которое больше, чем 19999999h ;; ecx - содержит число переведённых разрядов ;; в стеке лежит старшая часть числа. ;; Поскольку теперь уже всё равно придётся иметь дело с умножением 64 битного ;; числа, мы делаем приведение pop eax ;; eax = старшая часть числа call correct ;; Теперь edx:eax - содержат 64-bits текущее число. mov ecx,eax ;; Прибавляем последний разряд xor eax,eax lodsb sub al,30h jb short @F ;; Если меньше -- конец cmp al,9 ja short @F ;; старшая часть числа * 10 ;; младшая * 10 push eax mov eax,ecx mov ecx,edx shl ecx,1 mul CONSTANT_10 lea ecx,[ecx+ecx*4] add edx,ecx pop ecx ;; edx:eax = edx:eax + ecx (ecx = последний символ - 30h) add eax,ecx adc edx,0 ;; Преобразование законченно xor ecx,ecx mov cl,[esi] inc esi ret ;; Управление приходит на эту метку, ;; если мы вернулись при преобразовании последнего символа @@: add al,30h xchg ecx,eax ret ;; Управление приходит сюда, если мы вернулись из первого цикла. endproc: xor eax,eax mov al,byte ptr [esi-1] mov ecx,eax mov eax,edx xor edx,edx ret Str_to_Int64 endp ;;######################################################## |
|
|
Дата: Сен 4, 2003 01:25:40 А хде учет знака и проскипывание пробелов и прочего щыта в начале строки, и регистры не сохраняются, помойму это неприемлимо, делал бы уж чтоб совместимо с стандартной сишной версией было. я остановился на таком варианте, по размеру не оптимизировалось вобще. Чувствую щас заплюют меня :) ;; crt atoi() implementation by Dr.Golova ;; mailto: dr_golova@pisem.net ;; compile it with NetWide Assembler (NASM) ;; nasmw.exe -o atoi.obj -f coff -L atoi.asm bits 32 atoi: push ebx mov ecx, dword [esp+8] ;; input string ptr ;; scan and skip leading spaces .skip_space: movzx eax, byte [ecx] cmp eax, ' ' jnz short .check_sign inc ecx jmp short .skip_space ;; check and save sign .check_sign: mov edx, eax ;; save posible sign byte xor ebx, ebx cmp eax, '-' jz short .check_char cmp eax, '+' jnz short .no_sign .check_char: inc ecx movzx eax, byte [ecx] .no_sign: sub eax, '0' cmp eax, 9 ja .non_digit lea ebx, [ebx+ebx*4] lea ebx, [eax+ebx*2] jmp short .check_char .non_digit: cmp edx, '-' ;; check sign byte jnz short .done neg ebx .done: mov eax, ebx pop ebx retn end |
|
|
Дата: Сен 4, 2003 02:49:22 Чувствую щас заплюют меня :) Пусть только попробуют! Горой встану :) Тем кто первыми код отправляет всегда труднее других. Им всё прощается. |
|
|
Дата: Сен 4, 2003 03:17:06 · Поправил: Black_mirror Данный вариант хотя и тормозной, не пропускает пробелы и не обрабатывает знак, зато ошибки не пройдут незамеченными (не число: ecx=0, или переполнение: esi указывает на цифру).
strtoeax:;(esi-строка):eax-число,esi-остаток строки,ecx-прочитанно цифр
xor eax,eax
xor ecx,ecx
.l0:
mul [.c10]
or edx,edx
jnz .exit
movzx edx,byte [esi]
sub dl,'0'
jc .err
cmp dl,10
jnc .err
add eax,edx
jc .err2
inc ecx
inc esi
jmp .l0
.err2:
sub eax,edx
.err:
xor edx,edx
.exit:
div [.c10]
ret
.c10 dd 10
|
|
|
Дата: Сен 4, 2003 10:55:15 Dr.Golova The Svin Black_mirror Нет, нет, нет... Моя вина. Надо было написать всё более полнее. Итак. Перед вами код четырёх функций преобразования. Это не просто функции, а так называемые Элементарные контекстные функции. Многие из нас привыкли, что преобразование числа в строку осуществляется отдельной функцией. Да, с точки зрения достижение максимума скорости такой подход был бы верен, однако с точки зрения совершенства архитектуры – нет. Перед вами не сами функции перевода строки в число или наоборот. Сами по себе переводить они не могут, ибо не учитывают ошибок, которые могут быть допущены при конвертировании (а если учитывают, то список этих ошибок мал). На основе этих элементарных контекстных функций могут быть основаны сложнейшие процедуры, начиная от банального перевода строк в числа и наоборот, заканчивая конвейерной обработкой данных в компиляторах ets. Интерфейсы этих функций построены таким образом, чтобы их было легко использовать как при преобразовании UNICODE, так и ANSI строк. Вам предлагается оптимизировать их по скорости либо по размеру, соблюдая жёсткие рамки интерфейса на входе и выходе функций. Однако вы можете: 1. Менять алгоритм 2. По-другому переплетать код В принципе интерфейс тоже можно изменить, если это позволяет улучшить производительность. ========================== Ошибки. Ошибоки не проверяются потому что их не нужно проверять. Это низшие функции, которыми пользуются шаблонизаторы, компиляторы. Там эти проверки осуществляются задолго ДО преобразовния. То есть тут нужно оптимизировать сами функции, оставляя суть входа. Хотя бы его. ОК, выход можно менять. |
|
|
Дата: Сен 4, 2003 13:59:29 Edmond Я вот немножко подправил код твоей первоначальной функции и получил это:
xor eax,eax
xor edx,edx
_scan_:
mov al,byte ptr [esi]
sub al,30h
jb _err_
cmp al,09h
ja _err_
shl edx,1
lea edx,[edx+edx*4]
add edx,eax
inc esi
jmp short _scan_
_err_:
add al,30h
ret
|
|
|
Дата: Сен 4, 2003 14:21:12 Ошибоки не проверяются потому что их не нужно проверять. Позвольте узнать как до преобразования проверить, что число на входе меньше чем 4294967296? По моему функция должна об этом честно сообщить, а не выдавать компилятору остаток от деления на 4294967296. |
|
|
Дата: Сен 4, 2003 14:51:55 Black_mirror А вот эта ошибка по моему проверяется!!! |
|
|
Дата: Сен 4, 2003 18:35:28 Edmond KiNDeR По моему первый jb - лишняя проверка... |
|
|
Дата: Сен 4, 2003 18:47:54 boozook А ноль как поймать??? |
|
|
Дата: Сен 4, 2003 18:53:00 KiNDeR А зачем inc esi??? lodsb это сама делает. А что shl edx,1 lea edx,[edx+edx*4] add edx,eax Быстрее чем lea edx,[edx+edx*4] lea edx,[eax+edx*2] Что скажет народ? |
|
|
Дата: Сен 4, 2003 18:57:12 · Поправил: boozook Edmond '0'-30h=0h <9 => все OK 00h-30h=-30h >9 => конец Dr.Golova тоже это заметил |
|
|
Дата: Сен 4, 2003 18:59:12 И тот и другой способ приводит к возникновению AGI |
|
|
Дата: Сен 4, 2003 19:01:16 boozook Молодец, и Dr.Golova тоже.. А я не заметил :))) Это значительный + |
|
|
Дата: Сен 4, 2003 19:15:46 Fixer Приводи то приводит. Это понятно. Но что лучше? |
|
Powered by miniBB 1.6 © 2001-2002
Время загрузки страницы (сек.): 0.076 |