· Начало · Отвђтить · Статистика · Поиск · FAQ · Правила · Установки · Язык · Выход · WASM.RU · Noir.Ru ·

 WASM Phorum —› WASM.A&O —› Str_to_Int

. 1 . 2 . 3 . 4 . >>

Посл.отвђт Сообщен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
Приводи то приводит. Это понятно.
Но что лучше?

. 1 . 2 . 3 . 4 . >>


Powered by miniBB 1.6 © 2001-2002
Время загрузки страницы (сек.): 0.076