Мне понадобилось написать для одной программки вставку, которая бы считала MD5. За образец взял псевдокод отсюда: http://en.wikipedia.org/wiki/MD5 . Что-то получилось. Но считает 60-70 секунд 700-метровый файл. Потом нашел вот этот исходник: http://www.langfine.com/downloads/MD5%20v1.2.zip и по его образцу сделал новую вставку. Время подсчета для того же файла упало секунды на три (причем там число обращений к RAM колоссально понизилось). Оба раза я читал файл чанками по 40 байт (ровно столько, сколько обрабатывается одной итерацией алгоритма). После этого решил переделать на подсчет с чтением файла по 64 КБ. Тем самым понизил время втрое, т.е. теперь на тот же файл уходит около 20 секунд (прикладываю файл, что в конце концов получилось). Есть одна программка eXpress CheckSum Calculator... она считает тот же файл примерно за десять секунд (причем подсчитывает за это время MD5, SHA-1 и CRC32). Т.е. мои 20 секунд еще понижать и понижать... секунд наверное до семи-восьми. В связи с этим у меня несколько вопросов: 1) Стоит ли считывать весь файл сразу целиком в память, а потом через него прогонять подсчет MD5? (код существенно упростится, но вдруг файл 3 ГБ занимает... будет ли достаточно политкорректно по отношению к компьютеру сразу так нахально забить память, и не отразится ли это негативно на производительности алгоритма?) 2) Имеет ли смысл использовать при этом кучу или лучше заменить на VirtualAlloc? 3) Имеет ли смысл заниматься перемешкой независимых инструкций или можно смело положиться на out-of-order execution? Прикладываю, что получилось (снабдил комментариями)... подскажите, что еще можно оптимизировать.
Забыл упомянуть... а если eXpress CheckSum Calculator считает только MD5, то секунды за три. twgt А разве MD5 подразумевает разбиение на потоки? Я не представляю, как ее на несколько потоков разбить. Там каждое следующее действие зависит от предыдущего.
l_inc Я имел виду что она ещё подсчитывает разные чексуммы. А у твоей программы md5-хэш совпадает с тем, который выдаёт eXpress CheckSum Calculator??
Хотя не... прогнал... наверное можно будет разбить небольшой кусок на четыре потока. Смешно было бы говорить о скорости выполнения, если программа вообще не работает. P.S. Точку в ссылке поправил.
Может по 64? Целиком точно не нужно. Грузи файл кусками от 4 до 16 Мб — и памяти немного занимает, и производительность хорошая. Ты буфер выделяешь один раз, поэтому это роли не играет. Имеет только в том случае, если больше оптимизировать нечего. При хешировании файла с диска смысла не имеет =) А вот прикрутить ко всему этому асинхронное чтение (чтобы хеш вычислялся во время чтения), я думаю, нужно. И вообще, погоняй под профайлером, посмотри сколько времени занимает чтение, а сколько само вычисление хеша. И сразу станет ясно что оптимизировать.
flankerx Ну да. По 40h байт. Тогда заменю на VirtualAlloc. Там хоть хэндл кучи хранить не надо. Спасибо. Думаю попробую. Если честно, то я о профайлерах только понаслышке, но мне ничего не стоит удалить кусок кода с подсчетом и оставить чисто чтение из файла, а потом замерять. twgt Блин. Знал бы, что есть такие апи, не мучался бы. Благодарю.
flankerx Я немного позамерял. Поменял на VirtualAlloc, читаю по 4 МБ. Получилось, что на чтение файла в чистом виде уходит около 1,2 секунды. А на полный подсчет около 26 секунд (20 секунд было при старом состоянии системы, поэтому эти 26 и те 20 никак не связаны). Т.е. выигрыш при асинхронном чтении будет измеряться в единицах процентов. Думаю я погорячился. Как разбить вычисление хэша на потоки, не представляю. Прикладываю последний вариант... может у кого нибудь будут идеи, что ускорить...
l_inc Ну всякие мелочи вместо Код (Text): mov reg,-1h ... MOVZX EBX,AH ....... ADD AL,-1 замени на Код (Text): xor rex,reg dec reg ... xor ebx,ebx xor BL,AH ........ dec AL хотя незнаю насколько это прибавит скорости :/
Код (Text): or reg,-1 Вообще советую отказаться от использования однобайтовых регистров с *h (ah,ch,dh...) для наименьшего геммора при переносе на 64х платформу.
Не понимаю чем плох mov reg, -1h. Берет один такт, как и or. И спариваем в обоих конвейерах. Хотя при замене я выиграл на 700 метрах около секунды. Насчет ADD AL,-1 и DEC AL мне 10110111 как-то написал вот это (о замене DEC ECX на SUB ECX,1): Не критично, но я не вижу повода делать обратную замену. А на замене MOVZX EBX,AH на Код (Text): xor ebx,ebx xchg BL,AH какие-то сотни миллисекунд даже потерял. У меня напряг с числом свободных регистров. А до переноса на 64х платформу еще жить и жить. IceStudent Собственно говорю, что вижу. Вот код, который на 700-х мегабайтах за 1,2 секунды выполняется: Код (Text): USE32 include 'tables.inc' ;INT3 PUSH EBP MOV EBP,ESP ;[EBP+8h] = Pointer to MD5Hash (out) ;[EBP+0Ch] = File path (in) ;[EBP+10h] = Pointer to APIList (in) (CreateFile,CloseHandle,GetFileSize,ReadFile,VirtualAlloc,VirtualFree) PUSH 0 ;lpNumberOfBytesRead buffer [EBP-4h] PUSH 0 ;hFile storage [EBP-8h] PUSH 0 ;pointer to the Memory [EBP-0Ch] PUSH 0 ;pointer to the current 512-bit message chunk [EBP-10h] PUSH 0 ;EOF flag [EBP-14h] PUSH EBX PUSH ESI PUSH EDI MOV EBX,[EBP+10h] ;EBX = Pointer to APIList ;Allocating memory PUSH 4h ;flProtect PUSH 1000h ;flAllocationType PUSH CHUNK_SIZE+40h ;dwSize PUSH 0h ;lpAddress CALL DWORD [EBX+10h] ;CALL VirtualAlloc MOV [EBP-0Ch],EAX MOV [EBP-10h],EAX ADD DWORD [EBP-10h],CHUNK_SIZE ;Opening file PUSH 0h ;hTemplateFile PUSH 0h ;dwFlagsAndAttributes PUSH 3h ;dwCreationDisposition PUSH 0h ;lpSecurityAttributes PUSH 0h ;dwShareMode PUSH 80000000h ;dwDesiredAccess PUSH DWORD [EBP+0Ch] ;lpFileName CALL DWORD [EBX] ;CALL CreateFile MOV [EBP-8h],EAX ;Initializing ABCD MOV EAX,[EBP+8h] MOV DWORD [EAX],067452301h MOV DWORD [EAX+4h],0EFCDAB89h MOV DWORD [EAX+8h],098BADCFEh MOV DWORD [EAX+0Ch],010325476h nextFChunk: ;Reading file chunk PUSH 0 ;lpOverlapped LEA EAX,[EBP-4h] PUSH EAX ;lpNumberOfBytesRead PUSH CHUNK_SIZE ;nNumberOfBytesToRead PUSH DWORD [EBP-0Ch] ;lpBuffer PUSH DWORD [EBP-8h] ;hFile MOV EBX,[EBP+10h] CALL DWORD [EBX+0Ch] ;CALL ReadFile ;Appending additional bits CMP DWORD [EBP-4h],CHUNK_SIZE JE notLastFChunk OR DWORD [EBP-14h],-1h notLastFChunk: CMP DWORD [EBP-14h],0 JE nextFChunk ;Closing handle to the file and destroying heap MOV EBX,[EBP+10h] PUSH DWORD [EBP-8h] ;hObject CALL DWORD [EBX+4h] ;CALL CloseHandle PUSH 8000h ;dwFreeType PUSH 0h ;dwSize PUSH DWORD [EBP-0Ch] ;lpAddress CALL DWORD [EBX+14h] ;CALL VirtualFree POP EDI POP ESI POP EBX MOV ESP,EBP POP EBP RETN 10h
l_inc Хм.. Оффтоп, конечно, но приведи характеристики железа. Откуда читаешь, с флешки? Или с файла в RAM Drive?
IceStudent masquer Просто меня эта скорость не удивила, и перепроверять я не стал. Видимо t00x прав. Т.к. при первом чтении файла уходит 18 секунд, а при всех повторных уже 1,2 секунды. Просто я всегда проверял сначала производительность кода, считающего MD5, а потом уже кода-бланка, который просто читал из файла. Тем не менее после кэширования виндой всего файла скорость выполнения кода, считающего MD5, почему-то не меняется (остается в районе 25-27 секунд). Интересная фишка с eXpress CheckSum Calculator'ом. Оказывается его три секунды - это уже по готовенькому : я всегда сначала тестировал своей прогой, а потом им. Поэтому он уже скэшированный файл получал. Попробовал его только что на новом файле: у него ушло аж 40 секунд! Теперь вопрос об оптимизации сводится к такому: почему моя программка подсчета MD5 не использует скэшированый файл и как ее заставить это делать? P.S. TermoSINteZ Не понял комментария.