Быстрый поиск
Введите фрагмент названия статьи для поиска
Оптимизация умножения на константу на Ассемблере
05.08.2013 | Категория: Образ мышления: Assembler | Автор: ManHunter
В предыдущей статье по оптимизации мы рассматривали замену команд деления, как это описано в "AMD Athlon Processor x86 Code Optimization Guide". Но в этом же мануале описана оптимизация умножения на константу. Тут нет универсальных алгоритмов, по которым можно было бы однозначно определить нужную последовательность команд замены, как в случае с делением. Для каждой константы набор команд определяется на основании их "весовых коэффициентов" и количества тактов процессора, необходимых для их выполнения. В качестве примера приведена вот такая табличка оптимизации умножения с 2 до 32. Ее всегда можно посмотреть и в самом мануале, но лучше пусть будет под рукой.Code (Assembler) : Убрать нумерацию
- ; Умножение на 2
- add reg1, reg1 ; 1 такт процессора
- ; Умножение на 3
- lea reg1, [reg1+reg1*2] ; 2 такта процессора
- ; Умножение на 4
- shl reg1, 2 ; 1 такт процессора
- ; Умножение на 5
- lea reg1, [reg1+reg1*4] ; 2 такта процессора
- ; Умножение на 6
- lea reg1, [reg1+reg1*2] ; 3 такта процессора
- add reg1, reg1
- ; Умножение на 7:
- mov reg2, reg1 ; 2 такта процессора
- shl reg1, 3
- sub reg1, reg2
- ; Умножение на 8:
- shl reg1, 3 ; 1 такт процессора
- ; Умножение на 9:
- lea reg1, [reg1+reg1*8] ; 2 такта процессора
- ; Умножение на 10:
- lea reg1, [reg1+reg1*4] ; 3 такта процессора
- add reg1, reg1
- ; Умножение на 11:
- lea reg2, [reg1+reg1*8] ; 3 такта процессора
- add reg1, reg1
- add reg1, reg2
- ; Умножение на 12:
- lea reg1, [reg1+reg1*2] ; 3 такта процессора
- shl reg1, 2
- ; Умножение на 13:
- lea reg2, [reg1+reg1*2] ; 3 такта процессора
- shl reg1, 4
- sub reg1, reg2
- ; Умножение на 14:
- lea reg2, [reg1+reg1] ; 3 такта процессора
- shl reg1, 4
- sub reg1, reg2
- ; Умножение на 15:
- lea reg1, [reg1+reg1*2] ; 4 такта процессора
- lea reg1, [reg1+reg1*4]
- ; Умножение на 16:
- shl reg1, 4 ; 1 такт процессора
- ; Умножение на 17:
- mov reg2, reg1 ; 2 такта процессора
- shl reg1, 4
- add reg1, reg2
- ; Умножение на 18:
- lea reg1, [reg1+reg1*8] ; 3 такта процессора
- add reg1, reg1
- ; Умножение на 19:
- lea reg2, [reg1+reg1*2] ; 3 такта процессора
- shl reg1, 4
- add reg1, reg2
- ; Умножение на 20:
- lea reg1, [reg1+reg1*4] ; 3 такта процессора
- shl reg1, 2
- ; Умножение на 21:
- lea reg2, [reg1+reg1*4] ; 3 такта процессора
- shl reg1, 4
- add reg1, reg2
- ; Умножение на 22:
- lea reg2, [reg1+reg1*4] ; 4 такта процессора
- add reg1, reg1
- lea reg1, [reg1+reg2*4]
- ; Умножение на 23:
- lea reg2, [reg1+reg1*8] ; 3 такта процессора
- shl reg1, 5
- sub reg1, reg2
- ; Умножение на 24:
- lea reg1, [reg1+reg1*2] ; 3 такта процессора
- shl reg1, 3
- ; Умножение на 25:
- lea reg1, [reg1+reg1*4] ; 4 такта процессора
- lea reg1, [reg1+reg1*4]
- ; Умножение на 26:
- lea reg2, [reg1+reg1*2] ; 4 такта процессора
- add reg1, reg1
- lea reg1, [reg1+reg2*8]
- ; Умножение на 27:
- lea reg1, [reg1+reg1*2] ; 4 такта процессора
- lea reg1, [reg1+reg1*8]
- ; Умножение на 28:
- lea reg2, [reg1*4] ; 3 такта процессора
- shl reg1, 5
- sub reg1, reg2
- ; Умножение на 29:
- lea reg2, [reg1+reg1*2] ; 3 такта процессора
- shl reg1, 5
- sub reg1, reg2
- ; Умножение на 30:
- lea reg2, [reg1+reg1] ; 3 такта процессора
- shl reg1, 5
- sub reg1, reg2
- ; Умножение на 31:
- mov reg2, reg1 ; 2 такта процессора
- shl reg1, 5
- sub reg1, reg2
- ; Умножение на 32:
- shl reg1, 5 ; 1 такт процессора
Читать статью целиком »
Просмотров: 8396 | Комментариев: 7
Замена деления умножением на Ассемблере
07.07.2013 | Категория: Образ мышления: Assembler | Автор: ManHunter
Замена деления умножением на Ассемблере
При написании на Ассемблере алгоритмов, использующих деление на константу, операции деления можно заменять на операции умножения. Зачем это надо? Дело в том, что процессор выполняет операцию умножения в несколько раз быстрее, чем операцию деления. К примеру, на умножение тратится 5-9 тактов процессора (знаковое умножение) или 4-8 (беззнаковое умножение), тогда как для деления необходимо 22-47 тактов (знаковое деление) или 17-41 тактов (беззнаковое деление). В высокооптимизированных алгоритмах, таких как шифрование, математические расчеты, архивация больших объемов данных, подсчет контрольных сумм и других подобных задачах, экономия каждого такта процессора может дать значительный прирост общей скорости работы программы.
В руководстве по оптимизации "AMD Athlon Processor x86 Code Optimization Guide" вопрос замены деления умножением расписан очень подробно. Рассмотрены частные случаи деления, такие как деление на степень двойки, описан выбор алгоритма для знакового и беззнакового деления и приведен код для вычисления вспомогательных корректирующих значений. Кроме этого, в руководстве описаны методы оптимизации и других математических операций. Даже если вы никогда не будете использовать на практике замену деления умножением, почитать о других способах оптимизации будет очень полезно.
Читать статью целиком »
Просмотров: 14668 | Комментариев: 4
Расчет энтропии на Ассемблере
29.06.2012 | Категория: Образ мышления: Assembler | Автор: ManHunter
Энтропия - это количество информации, приходящейся на одно элементарное сообщение источника, вырабатывающего статистически независимые сообщения. Говоря проще, это условный коэффициент, показывающий распределение уникальных элементов в массиве данных. На практике, к примеру, анализаторы исполняемых файлов используют подсчет энтропии секции кода как часть эвристики, для определения насколько упакованы или зашифрованы файлы. Чем выше показатель энтропии - тем выше вероятность, что данные уже упакованы или зашифрованы, и тем меньше они могут быть подвергнуты повторной компрессии. Математическая формула расчета энтропии следующая:Формула расчета энтропии
Где p(i) - количество каждого уникального символа в строке, разделенное на общую длину строки. Программно это легко реализуется. Так, строка '012345' имеет показатель энтропии 2.58, строка '001122', так же как и '222200001111' - 1.58, а строка любой длины, состоящая из одинаковых символов, имеет нулевую энтропию. Я написал такую функцию на Ассемблере для расчета энтропии произвольного блока памяти:
Code (Assembler) : Убрать нумерацию
- ;-----------------------------------------------------------------------
- ; Функция расчета энтропии блока памяти
- ; by ManHunter / PCL
- ; http://www.manhunter.ru
- ;
- ; Параметры:
- ; szStr - указатель на блок памяти для расчета
- ; dLen - размер блока в байтах
- ; lpRes - указатель на приемник результата (DWORD)
- ;-----------------------------------------------------------------------
- proc CalcEntropy szStr:DWORD, dLen:DWORD, lpRes:DWORD
- locals
- count dd ? ; Количество символов
- cnt rd 256 ; Счетчики символов
- endl
- pusha
- ; Инициализация сопроцессора
- finit
- ; Обнулить счетчики символов
- lea edi,[cnt]
- mov ecx,256
- xor eax,eax
- rep stosd
- ; Подсчет количества символов
- mov esi,[szStr]
- mov ecx,[dLen]
- .loc_count_chars:
- xor eax,eax
- lodsb
- shl eax,2
- inc dword [cnt+eax]
- loop .loc_count_chars
- ; Начальное значение энтропии
- fldz
- ; Расчет энтропии для каждого символа
- mov ecx,256
- .loc_calc_entr:
- dec ecx
- ; Получить количества символов
- mov eax,ecx
- shl eax,2
- mov eax,[cnt+eax]
- ; Нулевые количества пропускаем
- or eax,eax
- jz @f
- mov [count],eax
- fild [dLen] ; Длина строки
- fild [count] ; Количество символов
- fdiv st0,st1 ; P(i) = SUM(i)/total
- fst st1 ; Скопировать st0 в st1
- fchs ; Изменить знак
- fxch st1 ; Поменять местами регистры
- fyl2x ; H(i) = -P(i)*log2(P(i))
- fadd st1,st0 ; H = H+H(i)
- ffree st0
- fincstp ; Почистить стек
- @@:
- ; Все символы обработали?
- or ecx,ecx
- jnz .loc_calc_entr
- ; Записать значение в ячейку памяти
- mov eax,[lpRes]
- fstp dword [eax]
- ffree st0
- fincstp ; Почистить стек
- popa
- ret
- endp
Читать статью целиком »
Просмотров: 6258 | Комментариев: 1
Преобразование строки в вещественное число на Ассемблере
31.05.2012 | Категория: Образ мышления: Assembler | Автор: ManHunter
Про преобразование строки в обычное число я уже писал, теперь задача посложнее - надо преобразовать строку в вещественное число с плавающей запятой (float). Для этого я написал вот такую функцию преобразования:Code (Assembler) : Убрать нумерацию
- ;------------------------------------------------------------
- ; Функция преобразования строки в вещественное число
- ; by ManHunter / PCL
- ; http://www.manhunter.ru
- ;
- ; Параметры:
- ; lpStr - указатель на исходную строку в формате ASCIIZ
- ; lpResult - указатель на переменную-приемник значения
- ; На выходе:
- ; EAX = 1 - строка успешно преобразована
- ; EAX = 0 - строка не может быть преобразована в число
- ;
- ; Примечание: переменная-приемник может иметь размер DWORD
- ; или QWORD, в зависимости от этого должна изменяться и
- ; функция (см. комментарии в конце кода). По умолчанию
- ; считается, что переменная имеет размер DWORD
- ;------------------------------------------------------------
- proc string2float lpStr:DWORD, lpResult:DWORD
- ; Локальные переменные
- locals
- dot dd ? ; Указатель на дробную часть
- exp dd ? ; Указатель на экспоненту
- digit dd ? ; Цифра
- endl
- pusha
- ; Проверка строки на валидность
- mov [digit],1
- mov [exp],0
- mov [dot],0
- mov esi,[lpStr]
- ; Минус или плюс может быть только в начале
- cmp byte [esi],'-'
- je @f
- cmp byte [esi],'+'
- jne .loc_chk_loop
- @@:
- inc esi
- ; После знака не может быть нуля
- cmp byte [esi],0
- je .loc_chk_error
- .loc_chk_loop:
- ; В строке должны быть цифр, экспонента и не более одной точки
- lodsb
- or al,al
- jz .loc_chk_complete
- cmp al,'e'
- je .loc_chk_exp
- cmp al,'E'
- je .loc_chk_exp
- cmp al,'.'
- je .loc_chk_dot
- cmp al,'0'
- jb .loc_chk_error
- cmp al,'9'
- ja .loc_chk_error
- jmp .loc_chk_loop
- .loc_chk_dot:
- ; Точка в строке уже есть?
- cmp [dot],0
- ; Строка имеет некорректный формат
- jne .loc_chk_error
- ; Экспонента уже есть?
- cmp [exp],0
- ; Строка имеет некорректный формат
- jne .loc_chk_error
- ; Указатель на дробную часть
- mov [dot],esi
- jmp .loc_chk_loop
- .loc_chk_exp:
- ; Экспонента уже есть?
- cmp [exp],0
- ; Строка имеет некорректный формат
- jne .loc_chk_error
- ; Указатель на начало экспоненты
- mov [exp],esi
- ; Сразу после экспоненты не может быть нуля
- cmp byte [esi],0
- je .loc_chk_error
- ; После экспоненты может быть знак
- cmp byte [esi],'-'
- je @f
- cmp byte [esi],'+'
- jne .loc_chk_loop
- @@:
- inc esi
- ; Сразу после минуса не может быть нуля
- cmp byte [esi],0
- je .loc_chk_error
- ; Проверить следующий символ
- jmp .loc_chk_loop
- .loc_chk_error:
- ; Строка не является числом
- mov [digit],0
- jmp .loc_ret
- .loc_chk_complete:
- ; Инициализация сопроцессора
- finit
- ; Начальное значение числа
- fldz
- ; Множитель и делитель
- mov [digit],10
- fild dword [digit]
- ; Запись значений до запятой
- mov esi,[lpStr]
- ; В начале строки минус?
- cmp byte [esi],'-'
- je @f
- cmp byte [esi],'+'
- jne .loc_before_dot
- @@:
- inc esi
- ; Преобразование числа до запятой
- .loc_before_dot:
- lodsb
- ; Конец строки?
- or al,al
- jz .loc_complete
- cmp al,'.'
- je .loc_complete_before_dot
- cmp al,'e'
- je .loc_exp
- cmp al,'E'
- je .loc_exp
- ; Очередная цифра
- sub al,'0'
- movzx eax,al
- mov [digit],eax
- ; Записать
- fild dword [digit]
- fxch st2
- fmul st0,st1
- fxch st2
- fadd st2,st0
- ffree st0 ; Почистить стек
- fincstp
- jmp .loc_before_dot
- ; Преобразование дробной части числа
- .loc_complete_before_dot:
- ; Дробная часть есть?
- cmp [dot],0
- je .loc_complete_after_dot
- ; Экспонента есть?
- cmp [exp],0
- je @f
- ; Указатель на начало экспоненты
- mov esi,[exp]
- jmp .loc_start_after_dot
- @@:
- ; Иначе перенести указатель на конец строки
- xor ecx,ecx
- dec ecx
- xor eax,eax
- mov edi,esi
- repne scasb
- mov esi,edi
- .loc_start_after_dot:
- std
- dec esi
- dec esi
- ; Дробная часть
- fldz
- fxch st1
- .loc_after_dot:
- lodsb
- ; Конец дробной части?
- cmp al,'.'
- je .loc_complete_after_dot
- ; Очередная цифра
- sub al,'0'
- movzx eax,al
- mov [digit],eax
- ; Записать
- fild dword [digit]
- fadd st2,st0
- fxch st2
- fdiv st0,st1
- fxch st2
- ffree st0 ; Почистить стек
- fincstp
- jmp .loc_after_dot
- .loc_complete_after_dot:
- ; Сбросить флаг направления
- cld
- ffree st0 ; Почистить стек
- fincstp
- ; Сложить дробную и целую часть
- fadd st1,st0
- .loc_exp:
- ; Экспонента есть?
- cmp [exp],0
- je .loc_complete
- ; Получить значение экспоненты
- xor ecx,ecx
- mov esi,[exp]
- ; В начале строки минус?
- cmp byte [esi],'-'
- je @f
- cmp byte [esi],'+'
- jne .loc_start_exp
- @@:
- inc esi
- .loc_start_exp:
- lodsb
- or al,al
- jz .loc_end_exp
- sub al,'0'
- movzx eax,al
- imul ecx,10
- add ecx,eax
- jmp .loc_start_exp
- .loc_end_exp:
- or ecx,ecx
- jz .loc_complete
- ffree st0 ; Почистить стек
- fincstp
- mov [digit],10
- fild dword [digit]
- ; Делить или умножать?
- mov esi,[exp]
- cmp byte [esi],'-'
- je .loc_exp_divide
- .loc_exp_multiple:
- fmul st1,st0
- loop .loc_exp_multiple
- jmp .loc_complete
- .loc_exp_divide:
- fdiv st1,st0
- loop .loc_exp_divide
- .loc_complete:
- ffree st0 ; Почистить стек
- fincstp
- ; В начале строки минус?
- mov esi,[lpStr]
- cmp byte [esi],'-'
- jne @f
- ; Изменить знак числа
- fchs
- @@:
- ; Записать значение в ячейку памяти
- mov eax,[lpResult]
- ; Если требуется повышенная точность, то приемник
- ; должен иметь размер QWORD, а следующую команду
- ; надо заменить на fstp qword [eax]
- fstp dword [eax]
- ; Успешное преобразование
- mov [digit],1
- .loc_ret:
- popa
- ; Результат преобразования
- mov eax,[digit]
- ret
- endp
Читать статью целиком »
Просмотров: 9249 | Комментариев: 13
Замена подстроки в строке на Ассемблере
28.03.2012 | Категория: Образ мышления: Assembler | Автор: ManHunter
Во всех языках высокого уровня среди функций работы со строками присутствуют функции замены заданной подстроки в строке. В Ассемблере такой функции нет, как нет ее и среди функций стандартных библиотек. Замена подстроки на строку такой же длины обычно сложностей не составляет, так как ее можно выполнить прямо в исходной строке без выделения дополнительной памяти. Замена на строку произвольной длины, в том числе и пустую, будет посложнее. Для этого я написал следующую функцию.Code (Assembler) : Убрать нумерацию
- ;-----------------------------------------------------
- ; Функция замены подстроки в строке
- ;-----------------------------------------------------
- ; lpSrc - указатель на исходную строку
- ; lpDst - указатель на буфер для полученной строки
- ; lpPattern - указатель на заменяемую подстроку
- ; lpReplace - указатель на строку для замены
- ; dNum - количество замен (0 - заменить все)
- ;-----------------------------------------------------
- proc _replace lpSrc:DWORD, lpPattern:DWORD, lpReplace:DWORD,\
- lpDst:DWORD, dNum:DWORD
- pusha
- ; Указатель на буфер-приемник
- mov edx,[lpDst]
- ; Счетчик замен
- xor ebx,ebx
- ; Исходная строка не пустая?
- mov ecx,[lpSrc]
- cmp byte [ecx],0
- jz .loc_ret
- ; Заменяемая строка не пустая?
- mov eax,[lpPattern]
- cmp byte [eax],0
- jz .loc_copy_all
- .loc_scan:
- mov esi,ecx
- mov edi,[lpPattern]
- ; Исходная строка закончилась?
- cmp byte [esi],0
- je .loc_end_replace
- @@:
- ; Строки совпали с паттерном?
- cmp byte [edi],0
- je .loc_move_replace
- ; Символ совпадает с
- lodsb
- ; Заменять все вхождения?
- cmp [dNum],0
- je .loc_skip_counter
- ; Уже заменили нужное количество?
- cmp ebx,[dNum]
- je .loc_move_one_char
- .loc_skip_counter:
- cmp al,byte [edi]
- jne .loc_move_one_char
- inc edi
- jmp @b
- .loc_move_replace:
- ; Увеличить счетчик замен
- inc ebx
- mov ecx,esi
- ; Записать заменяющую строку
- mov esi,[lpReplace]
- mov edi,edx
- @@:
- lodsb
- or al,al
- jz .loc_scan
- stosb
- inc edx
- jmp @b
- .loc_move_one_char:
- ; Скопировать один символ
- mov al,byte [ecx]
- mov byte [edx],al
- inc edx
- inc ecx
- jmp .loc_scan
- .loc_end_replace:
- ; Записать финальный 0 в строку
- mov byte [edx],0
- jmp .loc_ret
- .loc_copy_all:
- ; Просто скопировать исходную строку
- mov esi,[lpSrc]
- mov edi,[lpDst]
- @@:
- lodsb
- stosb
- or al,al
- jnz @b
- .loc_ret:
- popa
- ret
- endp
Читать статью целиком »
Просмотров: 11651 | Комментариев: 8