Замена деления умножением на Ассемблере
Замена деления умножением на Ассемблере
При написании на Ассемблере алгоритмов, использующих деление на константу, операции деления можно заменять на операции умножения. Зачем это надо? Дело в том, что процессор выполняет операцию умножения в несколько раз быстрее, чем операцию деления. К примеру, на умножение тратится 5-9 тактов процессора (знаковое умножение) или 4-8 (беззнаковое умножение), тогда как для деления необходимо 22-47 тактов (знаковое деление) или 17-41 тактов (беззнаковое деление). В высокооптимизированных алгоритмах, таких как шифрование, математические расчеты, архивация больших объемов данных, подсчет контрольных сумм и других подобных задачах, экономия каждого такта процессора может дать значительный прирост общей скорости работы программы.
В руководстве по оптимизации "AMD Athlon Processor x86 Code Optimization Guide" вопрос замены деления умножением расписан очень подробно. Рассмотрены частные случаи деления, такие как деление на степень двойки, описан выбор алгоритма для знакового и беззнакового деления и приведен код для вычисления вспомогательных корректирующих значений. Кроме этого, в руководстве описаны методы оптимизации и других математических операций. Даже если вы никогда не будете использовать на практике замену деления умножением, почитать о других способах оптимизации будет очень полезно.
Сперва рассмотрим оптимизацию беззнакового целочисленного деления. Здесь есть несколько вариантов в зависимости от значений делителя. Первый вариант - если делитель находится в диапазоне от 1 до (231 - 1):
Code (Assembler) : Убрать нумерацию
- ; В описании используются следующие вспомогательные значения:
- ; m = множитель (multiplier)
- ; s = коэффициент смещения (shift factor)
- ;-----------------------------------------------------
- ; Алгоритм №0
- ;-----------------------------------------------------
- mov eax, m
- mul делитель
- shr edx, s
- ; EDX = частное
- ;-----------------------------------------------------
- ; Алгоритм №1
- ;-----------------------------------------------------
- mov eax, m
- mul делитель
- add eax, m
- adc edx, 0
- shr edx, s
- ; EDX = частное
Второй вариант беззнакового деления - когда делитель находится в интервале от 231 до (232 - 1). В этом случае частное может принимать только два значения - 0 или 1. Соответственно, оптимизированный алгоритм будет иметь следующий вид:
Code (Assembler) : Убрать нумерацию
- ; EAX = делимое
- xor edx, edx
- cmp eax, делитель ; CF = (делимое < делитель) ? 1 : 0
- sbb edx, -1 ; частное = 0+1-CF = (делимое < делитель) ? 0 : 1
- ; EDX = частное
Code (Assembler) : Убрать нумерацию
- ; EAX = делимое
- cmp eax, делитель ; CF = (делимое < делитель) ? 1 : 0
- mov eax, 0
- sbb eax, -1 ; частное = 0+1-CF = (делимое < делитель) ? 0 : 1
- ; EAX = частное
На всякий случай напомню частные случаи беззнакового целочисленного деления. На ноль натуральные числа делить нельзя, это без вариантов, так что и про оптимизацию тут речи быть не может. При делении на единицу частное всегда равно делимому, это вы тоже должны помнить еще со школьной скамьи. Деление на степень двойки в Ассемблере оптимизируется с использованием команды битового сдвига вправо SHR reg, степень_двойки:
Code (Assembler) : Убрать нумерацию
- ; EAX = делимое
- shr eax,1 ; EAX / 2
- shr eax,2 ; EAX / 4
- shr eax,3 ; EAX / 8
- shr eax,4 ; EAX / 16
- ; и так далее
Code (Assembler) : Убрать нумерацию
- ; EAX = делимое
- shr eax,0 ; EAX / 1
- ; EAX остался неизменным
Code (Assembler) : Убрать нумерацию
- ; В описании используются следующие вспомогательные значения:
- ; m = множитель (multiplier)
- ; s = коэффициент смещения (shift factor)
- ;-----------------------------------------------------
- ; Алгоритм №0
- ;-----------------------------------------------------
- mov eax, m
- imul делитель
- mov eax, делитель
- shr eax, 31
- sar edx, s
- add edx, eax
- ; EDX = частное
- ;-----------------------------------------------------
- ; Алгоритм №1
- ;-----------------------------------------------------
- mov eax, m
- imul делитель
- mov eax, делитель
- add edx, eax
- shr eax, 31
- shr eax, s
- add edx, eax
- ; EDX = частное
Частные случаи целочисленного знакового деления также отличаются от беззнакового. Вот, например, деление на двойку и степени двойки с разными знаками.
Code (Assembler) : Убрать нумерацию
- ;-----------------------------------------------------
- ; Знаковое деление на 2
- ;-----------------------------------------------------
- ; EAX = делимое
- cmp eax, 800000000h
- sbb eax, –1
- sar eax, 1
- ; EAX = частное
- ;-----------------------------------------------------
- ; Знаковое деление на 2^n
- ;-----------------------------------------------------
- ; EAX = делимое
- cdq
- and edx, (2^n–1)
- add eax, edx
- sar eax, n
- ; EAX = частное
- ;-----------------------------------------------------
- ; Знаковое деление на -2
- ;-----------------------------------------------------
- ; EAX = делимое
- cmp eax, 800000000h
- sbb eax, –1
- sar eax, 1
- neg eax
- ; EAX = частное
- ;-----------------------------------------------------
- ; Знаковое деление на -(2^n)
- ;-----------------------------------------------------
- ; EAX = делимое
- cdq
- and edx, (2^n–1)
- add eax, edx
- sar eax, n
- neg eax
- ; EAX = частное
AMD Athlon Processor x86 Code Optimization Guide
AMD.Athlon.Processor.x86.Code.Optimization.Guide.zip (1,047,105 bytes)
AMD.Athlon.Processor.x86.Code.Optimization.Guide.zip (1,047,105 bytes)
В исходниках и мануале упоминается статья Torbjorn Granlund "Division by Invariant Integers using Multiplication", на всякий случай вот ее полный текст (на английском).
Torbjorn Granlund - Division by Invariant Integers using Multiplication
Torbjorn.Granlund.Division.by.Invariant.Integers.using.Multiplication.zip (152,759 bytes)
Torbjorn.Granlund.Division.by.Invariant.Integers.using.Multiplication.zip (152,759 bytes)
В приложении скомпилированные программы из мануала для знакового и беззнакового деления, а также утилиты Magic Divider от The Svin и Muldiv 1.0 от bart^xt.
Утилиты для расчета замены деления умножением
Division.by.Multiplication.Utilities.zip (20,895 bytes)
Division.by.Multiplication.Utilities.zip (20,895 bytes)
Просмотров: 14553 | Комментариев: 4
Внимание! Статья опубликована больше года назад, информация могла устареть!
Комментарии
Отзывы посетителей сайта о статье
ManHunter
(23.07.2013 в 15:01):
Да, так правильнее. Спасибо.
kw33
(23.07.2013 в 13:18):
На мой взгляд название zip-архива должно быть не "multiplication by division", а "division by multiplication".
sim31
(18.07.2013 в 13:42):
Если девушка на картинке поняла хоть половину написанного на доске она гений, кроме того что... :) Да и автор блога тоже светлая голова, IQ наверное более 150, или таких 1:5000 что тоже самое. Какую статью не взять достаточно интересно )
Zhelezyaka
(09.07.2013 в 08:07):
Спасибо, как говорится - мотаем на ус,
и картинка клёвая, да - таких нужно умножать, а не делить )))
и картинка клёвая, да - таких нужно умножать, а не делить )))
Добавить комментарий
Заполните форму для добавления комментария