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

 WASM Phorum —› WASM.A&O —› Вычисление CRC32

<< . 1 . 2 .

Посл.отвђт Сообщенiе


Дата: Апр 25, 2004 15:31:28

Eraser
Извини за дурость, а ты скорость чем измеряешь и в чем? :)))


Дата: Апр 25, 2004 22:24:04

[Eraser: 2) Loop || dec ECX, jnz @@_for действительно пофигу.]
а ты думаешь у всех p4-1600??? Например у моего друга 486 проц стоит... если так относиться к этому делу, то уж лучше на VB писать, типа все равно процы быстрые :))) зачем тогда вообще все это затеял?


Дата: Апр 26, 2004 08:25:37 · Поправил: Eraser

2 EvilsInterrupt
Смотри сообщение от Апр 24, 2004 12:29:57.
2 Mad_C
Я не собираюсь "кидать" счастливых обладателей 486'х, просто я подтверждал _Chingachguk_'a
Что Loop полезнее DEC ECX, JNZ @@_Метка на P120, а на P800 зто мало влияет на скорость.
2All
А что на счёт передачи сложных структур FastCall'om, ответит кто нибудь?
см три сообщения назад!


Дата: Апр 26, 2004 11:25:38

А что на счёт передачи сложных структур FastCall'om, ответит кто нибудь?
А что там отвечать? Как в eax или edx указатель на локальную структуру поместить? :)

Вообще-то dec ecx, jnz побыстрее loop выходит, имхо


Дата: Апр 26, 2004 13:12:49 · Поправил: S_T_A_S_

Привет :)
Оптимизируем inner loops ?

Так, смотрим условие задачи:
-Прграмма загружает файл на 81mb в память. . Понятно.
        AND ECX,000000FFH
        MOV ECX,DWORD PTR [CRC_32_TAB+ECX*4]
Еще есть табличка 256*4байт. Тоже понятно.
К тому же не понятно, выровняна ли она по границе 256байт или нет?

Дальше смотрим, например:
P4 2600Mhz, 256mb DDR -результат 812, 250, 460 тиков.

Ядро проца 2600Mhz, а память? 800 в случае DUAL DDR.
Разница в скорости есть.

Выводы:
- узкое место - пропускная способность шины памяти.
- поскольку используется обращзение к 2м блокам памяти неизбежно возникают конфликты банков кеша, что приводит к лишним операциям записи/чтения.

Что же делать?
- убрать табличку, если это возможно.
- использовать предвыборку данных из памяти в кеш, для того чтобы ускорить их обработку.

Для реализации 2го предложения алгоритм работы меняем на следующий:
- по возможности "разворачиваем направление" чтения из памяти (т.е. с конца к началу)
- основной цикл разбиваем примерно так:
CACHELINE	equ	20h	;; for Celerons etc
CACHESIZE	equ	20000h	;; 128Kb for celeron/duron
	mov	ESI, source array
	mov	ECX, number of bytes
	lea	ESI, [ESI+ECX]
	neg	ECX
.l:	add	ECX, CACHESIZE	;;  move up to end of cashed block
	mov	EAX, CACHESIZE / (CACHELINE*2)	;;  цикл развернут 2X
;;  читаем данные в кеш процессора..
@@:		mov	ebx, [ESI+ECX-CACHELINE]	;;  ..для этого читаем байт (из памяти считается строка кеша полностью)...
		mov	ebx, [ESI+ECX-CACHELINE*2]	;;  ... и еще для предыдущей строки кеша
		sub	ECX, CACHELINE*2
		dec	EAX
	jnz	@b
;;  use data block from the cache
	mov	EAX, CACHESIZE / 64
@@:		;;  ЗДЕСЬ ОБРАБАТЫВАМ 64 байта данных
		;;  по адресам	[esi+ecx] .. [esi+ecx+63]
		;;  если будет другое кол-во, меняем значение в ECX ниже и EAX выше
		;;  для примера цикл развернут
		add	ECX, 64
		dec	EAX
	jnz	@b
	or	ECX, ECX
	jnz	.l


Иллюстрация разницы в скорости операций с памятью при использовании подобного приема здесь (не работает на старых процах из-за команд записи) Теоретические основы (англ. с картинками)

ЗЫ
Пинания меня приветствуются :)


Дата: Апр 28, 2004 15:01:29 · Поправил: Eraser

Если я правильно понял, при MOV EBX,[ESI] в кэш читаются данные размером CACHELINE, от адреса ESI по ESI+CACHELINE и при последующем запросе данных по адресу находящемуся в пределах от ESI до ESI+CACHELINE они читаются не из памяти ,а из кэша что ускоряет весь процесс. Но попробовав этот метод я не увидел ни какого прироста скорости, при этом если у каждого проца свой кэшлайн то функция теряет универсальность и метод KISS + ко всему увеличивает её размер.

Вот что получилось у меня:
procedure IncPCRC32ASMCache(P:Pointer;Count:integer;var CRC:cardinal);assembler;
const
        LineSize = $40;
        TabSize = Length(CRC_32_TAB)*$20;
        CntLine = TabSize div LineSize;
asm
        CMP EDX,1
        JL @@_END//Если Count<1 выходим
        PUSH EBX
        PUSH ESI
        PUSH ECX

        LEA ESI,[CRC_32_TAB+TabSize]//ESI указывает на конец таблицы CRC_32_TAB
        MOV ECX,CntLine/2
@@LodCache:
          MOV EBX,[ESI-LineSize]
          MOV EBX,[ESI-LineSize*2]
          SUB ESI,LineSize*2
          DEC ECX
          JNZ @@LodCache
        POP ECX
        MOV EBX,[ECX]
        PUSH ECX
        MOV ECX,EDX
        MOV EDX,EAX
        MOV EAX,EBX
@@CalcCRC:
          XOR AL,BYTE PTR [EDX]
          INC EDX
          AND EAX,000000FFH
          MOV EAX,DWORD PTR [ESI+EAX*4]
          SHR EBX,8
          XOR EAX,EBX
          MOV EBX,EAX
          LOOP @@CalcCRC
        POP ECX
        MOV [ECX],EBX
        POP ESI
        POP EBX
@@_END:
end;

Т.К не знаю какой CACHELINE у P4 перепробовал от 16 до 256.


Дата: Апр 28, 2004 17:00:50 · Поправил: S_T_A_S_

[ Eraser : при MOV EBX,[ESI] в кэш читаются данные.. ..и при последующем запросе данных.. ..они читаются не из памяти, а из кэша что ускоряет весь процесс. ]

Да, принцип такой.
"Загружаем" блок из памяти в кеш в первом цикле и с ним уже работаем во втором.

Накидал простой тест
(само вычисление CRC я тут сильно упростил :)
CACHELINE	equ	20h	;; for Celerons etc
CACHESIZE	equ	20000h	;; 128Kb for celeron/duron
	mov	ESI, src ;;  source array
	mov	ECX, [EBP+4] ;;  number of bytes
	lea	ESI, [ESI+ECX]
	neg	ECX
.l:	add	ECX, CACHESIZE
	mov	EAX, CACHESIZE / (CACHELINE*2)
;;  prefetch data
@@:		test	ebx, [ESI+ECX-CACHELINE]  
		test	ebx, [ESI+ECX-CACHELINE*2]	 
		sub	ECX, CACHELINE*2
		dec	EAX
	jnz	@b
	mov	EAX, CACHESIZE / 64
@@:		xor	ebx,dword [esi+ecx]
		xor	ebx,dword [esi+ecx+4]
		xor	ebx,dword [esi+ecx+8]
		xor	ebx,dword [esi+ecx+12]
		xor	ebx,dword [esi+ecx+16]
		xor	ebx,dword [esi+ecx+20]
		xor	ebx,dword [esi+ecx+24]
		xor	ebx,dword [esi+ecx+28]
		xor	ebx,dword [esi+ecx+32]
		xor	ebx,dword [esi+ecx+36]
		xor	ebx,dword [esi+ecx+40]
		xor	ebx,dword [esi+ecx+44]
		xor	ebx,dword [esi+ecx+48]
		xor	ebx,dword [esi+ecx+52]
		xor	ebx,dword [esi+ecx+56]
		xor	ebx,dword [esi+ecx+60]
		add	ECX, 64
		dec	EAX
	jnz	@b
	or	ECX, ECX
	jnz	.l


Результаты для CPU AuthenticAMD @ 1657 MHz (AXP2k+, DDR2100)
(меряю не такты, что малоинформативно, а скорость чтения из памяти):

1372 Mb/sec ;; это код выше
624,5 Mb/sec ;; это, если просто убрать test ebx, [ESI+ECX-CACHELINE] и следующую строку

В общем-то это не до тягивает до 85% скорости DDR2100 (т.е. в теории должно быть 1700-1800 Mb/sec),
но разницу видно.


Почему разницы нет у вас? Есть же табличка - когда происходит чтение из нее, происходят конфликты банков кеша.
Если хотите поднять скорость - меняйте алгоритм. "Внутриннее циклы" надо оптимизировать в последнюю очередь.


> если у каждого проца свой кэшлайн то функция теряет универсальность

У P2/PIII 32 байта, у Athlon / PIV - 64.
В моем примере ипользуется 32, что дает универсальность (хотя несколько терянется эффективность на Athlon/PIV)


> метод KISS + ко всему увеличивает её размер

Обычно по другому оптимизировать по скорости не получается.
За все нужно платить :)


ЗЫ

Если всеже от таблички уйти нельзя - попробуйте все равно сделать 2 цикла.
Внутринний фиктивно читает из [EDX+32]
и потом обрабатывает 32 байта начиная с [EDX]
Тут можно будет поэкспериментировать немного со смещением для предвыборки и с кол-вом байт во внутреннем цикле
(64, 128, делать предвыборку из 2х, 4х строк)

А смотреть спариваются ли команды - imho в данном случае мало смысла,
пока этим командам нечего обрабатывать, выполняться они не будут.

634767297__read_test.exe


Дата: Июн 1, 2004 06:06:06 · Поправил: Black_mirror

Оптимизация по размеру:
crc_32:;(buf +4, len +8)
        push esi
	mov esi,[4+esp+4]
	mov ecx,[4+esp+8]
	xor eax,eax
	jecxz .l1
    .l0:
	mov edx,eax
	lodsb
	xor edx,eax
	shr eax,8
	xor eax,[crc_32_table+edx*4]
	loop .l0
    .l1:
	pop esi
	ret 8

<< . 1 . 2 .


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