Главная страница
Top.Mail.Ru    Яндекс.Метрика
Текущий архив: 2014.01.19;
Скачать: CL | DM;

Вниз

Интересная задача. Остаток от деления умножением   Найти похожие ветки 

 
DevilDevil ©   (2013-07-25 17:53) [0]

Все знают, что целочисленное деление можно сэмулировать умножением и сдвигом. На уровне ассемблера - одним умножением (забрать результат в EDX)

Но вот я стал рассуждать, есть ли простой способ через элементарные операции и хотя бы одно умножение - найти остаток от деления. Пока в тупике

Разумеется можно сначала найти частное. Потом домножить на делитель и результат вычесть из делимого. Но хочется надеяться на чудо в виде какого-то более эффективного решения :)


 
Rouse_ ©   (2013-07-25 19:19) [1]

Ты IDIV хочешь через IMUL проэмулировать?


 
Rouse_ ©   (2013-07-25 19:28) [2]

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

По крайней мере аналог INC EAX через NOT EAX/ NEG EAX эт точно оттуда


 
DevilDevil ©   (2013-07-25 22:49) [3]

> Rouse_ ©   (25.07.13 19:19) [1]
> Ты IDIV хочешь через IMUL проэмулировать?


думаешь не получится ?

> Rouse_ ©   (25.07.13 19:28) [2]
> Поищи в инете букварь "Алгоритмические трюки для программистов"


спсб
зазырю


 
megavoid ©   (2013-07-25 22:57) [4]

Академически и правда интересно стало, а что же противопоставить SHR (хотя и занимаюсь скриптовым вебом)

Вбил "assembler get the div", чертыхнулся, вбил "assembler get the mod" :)
первые два резалта - оно?
http://stackoverflow.com/questions/8021772/assembly-language-how-to-do-modulo
http://stackoverflow.com/questions/8231882/how-to-implement-the-mod-operator-in-assembly

The DIV instruction (and it"s counterpart IDIV for signed numbers) gives both the quotient and remainder (modulo). DIV r16 dives a 32-bit number in DX:AX by a 16-bit operand and stores the quotient in AX and the remainder in DX.


mov dx, 0    
mov ax, 1234
mov bx, 10
div bx       ; Divides 1234 by 10. DX = 4 and AX = 123


А уж оптимизировать сие - это именно к Rouse :)


 
DevilDevil ©   (2013-07-25 23:18) [5]

> megavoid ©   (25.07.13 22:57) [4]

div выполняется где то 20-30 тактов
а mul 3 такта


 
Sha ©   (2013-07-25 23:29) [6]

> Все знают, что целочисленное деление можно сэмулировать умножением и сдвигом...

Мы умножаем число примерно на (2^n)/m,
а затем выдвигаем дробную часть результата.

Если вместо сдвига отбросить целую часть при помощи and,
затем умножить на m, а только потом выдвинуть хвост,
то по идее мы получи модуль. Но так мы получим большую погрешность.

Тот же самый результат, но без погрешности, как раз дает указанный вариант

> Разумеется можно сначала найти частное.
> Потом домножить на делитель и результат вычесть из делимого.

При этом ничто не мешает найти частное умножением и сдвигом.


 
megavoid ©   (2013-07-25 23:30) [7]

Но ведь и конвейеры нынче крутые, глядишь, оно и само там соптимизирует как лучше


 
Sha ©   (2013-07-25 23:32) [8]

Разумеется, для особых чисел (2^n и близких к ним) существуют особые алгоритмы.


 
DevilDevil ©   (2013-07-26 09:38) [9]

> megavoid ©   (25.07.13 23:30) [7]
> Но ведь и конвейеры нынче крутые, глядишь, оно и само там
> соптимизирует как лучше


Поэтому программы, которые летали на вторых пентиумах, по мере развития стали тормозить на i3. Подход неверный


 
Rouse_ ©   (2013-07-26 10:03) [10]


> DevilDevil ©   (25.07.13 22:49) [3]
> думаешь не получится ?

Получиться то получится, а вот будет ли быстрее - тут не уверен...


 
DevilDevil ©   (2013-07-26 15:52) [11]

> Rouse_ ©   (26.07.13 10:03) [10]

> Получиться то получится, а вот будет ли быстрее - тут не
> уверен...


я честно говоря ещё не пробовал, но почему сомнения ?
даже если придётся делать особые манипуляции в случае знака - всё решаемо командами типа mov/add/sub/lea/movcc/sar - т.е. даже без прыжков. По тактам мне кажется сложно будет набрать 20-30


 
Inovet ©   (2013-07-26 17:27) [12]

В Иньел не лохи процессоры проектируют, уж сделали и так ооптимально. Стоит вспомнить сколько тактов на деление было вначале 86-тых процессоров, и на умножение заодно вспомнить. Для частных случаев может и получится меньше, при написании этих случаев отдельно.

А уж если вспомнить 8080, так совсем радоваться только остаётся без всяких таблеток.


 
Rouse_ ©   (2013-07-26 17:28) [13]


> DevilDevil ©   (26.07.13 15:52) [11]
> я честно говоря ещё не пробовал, но почему сомнения ?

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


 
asail ©   (2013-07-26 18:04) [14]


> DevilDevil ©   (26.07.13 09:38) [9]

> Поэтому программы, которые летали на вторых пентиумах, по
> мере развития стали тормозить на i3. Подход неверный

Это какие такие программы? Сильно сомневаюсь, что Фотошоп 3 на П2 произвел бы одну и ту же операцию над одним и тем же изображением быстрее, чем CS4 на i3...
И подход абсолютно верный в 99% случаев. Вот смотри... Ты и твой конкурент решили написать одну и туже софтину. Он написал за год, не сильно заморачиваясь оптимизацией. Получилось вполне терпимо в плане скорости и он начал ее распространять. Ты же, вылизывал производительность 3 года, и в результате получил софтину значительно превосходящую конкурента по производительности. Но! Фиг ты ее теперь продашь, бо все твои потенциальные клиенты уже сидят на софте конкурента и платить дополнительно за тот же функционал им уже не охота. В остатке имеем, что ты вложил средств в 3 раза больше конкурента, а выхлоп - 0. А конкурент летает на собственном самолете на канары...


 
DevilDevil ©   (2013-07-26 18:10) [15]

> asail ©   (26.07.13 18:04) [14]

да не нужно далеко ходить
Kaspersky, Opera, IE, Chrome, Delphi

заканчивай холивар
о плюсах и минусах качественной разработки и количественной все давным давно слышали. Оптить, конвейеры крутые, div сделает за 3 такта )))). Смешно


 
asail ©   (2013-07-26 18:19) [16]


> DevilDevil ©   (26.07.13 18:10) [15]

> да не нужно далеко ходить Kaspersky, Opera, IE, Chrome, Delphi

А что с ними не так? Ну, за касперского не скажу, а с остальными-то? Да, подтормаживают броузеры... Но, не кажется ли тебе, что связанно это в большей степени с увеличившейся сложностью контента? Страницы, которые ИЕ9 грузит 30 сек, ИЕ5 не смог-бы открыть вообще!
Ты еще досовский едит сравни с Ворд 2010... Первый, конечно, текстовый файлик на 2кб откроет быстрее... :)

> заканчивай холивар

Да ладно. Удачи! :)


 
DevilDevil ©   (2013-07-26 18:21) [17]

> asail ©   (26.07.13 18:19) [16]

нет, есть и обратные случаи
горячо мной любимый Windows 7 например
вот это действительно показатель как надо работать. Наверное наняли программистов, которые знают, сколько тактов делается div


 
Rouse_ ©   (2013-07-26 18:34) [18]


> DevilDevil ©   (26.07.13 18:21) [17]
> нет, есть и обратные случаи
> горячо мной любимый Windows 7 например
> вот это действительно показатель как надо работать.

Немного сумбурно. А ХР чем плох? Мне обе системы нравятся, но вот каких-то там скачков мегапроизводительности я чей-то не наблюдал.


 
Rouse_ ©   (2013-07-26 18:37) [19]

ЗЫ: даже наоборот, некоторые оконные контролы начали работать с тормозами (даже веточка была, щас сходу не найду но что-=то там по поводу SysListView32 было - моргает/дергается в services.msc кажется и еще куче программ)


 
DevilDevil ©   (2013-07-26 18:58) [20]

> Rouse_ ©   (26.07.13 18:34) [18]

Дизайн, юзерфрендли, работа с диском (слабое место в бытовых условиях)
В итоге получалось приятнее, удобнее, быстрее
Так надо работать


 
DevilDevil ©   (2013-07-26 18:58) [21]

* получилось


 
Rouse_ ©   (2013-07-26 19:09) [22]


> DevilDevil ©   (26.07.13 18:58) [20]
> > Rouse_ ©   (26.07.13 18:34) [18]
>
> Дизайн, юзерфрендли, работа с диском (слабое место в бытовых
> условиях)

первое и второе да, а работа с диском - нет, она в фоне его периодически дефрагментирует. Вроде как и плюс - а с другой стороны минус, я уже 4 диска поменял за 3 года (разные производители). Онь Jack128 или Ega23 могут быть свидетелями как у меня все морозит по полной периодически. Это с учетом того что железо при переходе на семеру менял целиком. А у жеки уже два харда полность осыпались под семеркой.
Правда диски все под хорошей нагрузкой, на домашнем компе такого не наблюдается (тьху-тьху)...


 
DevilDevil ©   (2013-07-26 19:18) [23]

ну хз
Delphi XE то у тебя надеюсь стало работать медленнее, чем Delphi 7 в былые времена )


 
Rouse_ ©   (2013-07-26 19:28) [24]

XE3 и XE4 стартуют медленно, из-за проверки активации (выбешивает) но я уже веду работу :)


 
Inovet ©   (2013-07-26 19:59) [25]

> [22] Rouse_ ©   (26.07.13 19:09)
> я уже 4 диска поменял за 3 года

Ничёсе. Магнитгых, не на флеш-памяти?


 
Sha ©   (2013-07-26 20:22) [26]

> Наверное наняли программистов, которые знают, сколько тактов делается div

у нас даде в начальной школе знают,
сколько надо вычитаний при делении столбиком


 
Rouse_ ©   (2013-07-26 20:40) [27]


> Inovet ©   (26.07.13 19:59) [25]
> > [22] Rouse_ ©   (26.07.13 19:09)
> > я уже 4 диска поменял за 3 года
>
> Ничёсе. Магнитгых, не на флеш-памяти?

обычных, не SSD


 
DevilDevil ©   (2013-07-27 00:42) [28]

в книге "Алгоритмические трюки для программистов" я не нашёл ничего по поводу остатка кроме стандартного Делимое - Частное * Делитель

но обнаружил, что вычисление "магического множителя" является достаточно сложной задачей, далеко не такой простой, как я полагал. Жаль только ассемблер там не интеловский


 
Cii   (2013-07-27 00:51) [29]

а какой?
на ZX80 тоже специфический был, команд, регисторов меньше, а суть одна.


 
DevilDevil ©   (2013-07-27 01:40) [30]

RISC


 
DevilDevil ©   (2013-07-27 12:17) [31]

Ребятки, что же такое получается
Получается мы, мастера и участники форума, не знаем, как оптимизировать в своих программах деление на константу. Современные С++ компиляторы это делают автоматически, а Delphi увы нет.

Бог с ним с остатком от деления. Предлагаю разобраться с алгоритмом деления на константу. На повестке дня:
- деление Cardinal на константу Cardinal
- деление Integer на константу Integer

Пока мои опыты должным успехом не увенчались.
Но вместе с тем удалось добыть код нахождения "магической константы" и сдвига. (опять таки не очень ясно как их применять)

type
 TDivideInfo = record
   Magic: integer;
   Shift: integer;
 end;

function FindDivideInfo(D: integer): TDivideInfo;
const
 two31 = dword(1 shl 31);
var
 p: integer;
 ad, anc, delta, q1, r1, q2, r2, t: dword;
begin
 ad := abs(D);
 t := two31 + dword(D shr 31);
 anc := t - 1 - t mod ad;
 p := 31;
 q1 := two31 div anc;
 r1 := two31 - q1*anc;
 q2 := two31 div ad;
 r2 := two31 - q2*ad;

 while (true) do
 begin
   p := p + 1;
   q1 := 2*q1;
   r1 := 2*r1;

   if (r1 >= anc) then
   begin
     q1 := q1 + 1;
     r1 := r1 - anc;
   end;

   q2 := 2*q2;
   r2 := 2*r2;

   if (r2 >= ad) then
   begin
     q2 := q2 + 1;
     r2 := r2 - ad;
   end;
   delta := ad - r2;

   if not ((q1 < delta) or ((q1 = delta) and (r1 = 0))) then break;
 end;

 Result.Magic := q2 + 1;
 if (D < 0) then Result.Magic := -Result.Magic;
 Result.Shift := p - 32;
end;


Слушаю предложения и результаты тестов


 
DevilDevil ©   (2013-07-29 11:21) [32]

чё, никому не интересно быстрое деление ?
<S>неужели мастера готовы мириться с 30 тактами деления</S>


 
Sha ©   (2013-07-29 11:37) [33]

те, кому интересно, давно уже "прошли" деление )


 
DevilDevil ©   (2013-07-29 12:05) [34]

> Sha ©   (29.07.13 11:37) [33]
> те, кому интересно, давно уже "прошли" деление )


по опыту предыдущих лет с подобными высказываниями
таки нет
недомастерам интереснее газонокосилки и водопровод

Глобально поднятый мною вопрос не из мира фантастики. Компиляторы от MS, Intel, GNU, Apple - 100500 лет уже выполняют эту работу автоматически. И отсутствие интереса к этому вопросу наталкивает меня на достаточно неприятные выводы, в которые мне не хочется верить. Хочется верить, что здесь есть программисты, а не кнопкошлёпы


 
Rouse_ ©   (2013-07-29 14:44) [35]


> чё, никому не интересно быстрое деление ?

быстрее чем что?


> Компиляторы от MS, Intel, GNU, Apple - 100500 лет уже выполняют
> эту работу автоматически.

Оть тебе компилятор от MS

A = A / 3;
00E813E0  mov         eax,dword ptr [A]  
00E813E3  cdq  
00E813E4  mov         ecx,3  
00E813E9  idiv        eax,ecx  
00E813EB  mov         dword ptr [A],eax  


а это вариант на Delphi

Unit2.pas.37: A := A div 3;
005B233A B903000000       mov ecx,$00000003
005B233F 8B45F4           mov eax,[ebp-$0c]
005B2342 99               cdq
005B2343 F7F9             idiv ecx
005B2345 8945F4           mov [ebp-$0c],eax


не наблюдаю разницы...


 
DevilDevil ©   (2013-07-29 15:04) [36]

> Rouse_ ©   (29.07.13 14:44) [35]

-O3


 
Sha ©   (2013-07-29 15:06) [37]

> отсутствие интереса к этому вопросу наталкивает меня на достаточно неприятные выводы

когда надо, интерес присутствует:
http://www.guildalfa.ru/alsha/node/6


 
DevilDevil ©   (2013-07-29 15:13) [38]

> Sha ©   (29.07.13 15:06) [37]

а так не канает ?

function IncDay(const DateTime: TDateTime;  NumberOfDays: Integer): TDateTime;
begin
 Result := DateTime + NumberOfDays;
end;


 
Sha ©   (2013-07-29 15:13) [39]

да ты че?


 
DevilDevil ©   (2013-07-29 15:21) [40]

> Rouse_ ©   (29.07.13 14:44) [35]
> Оть тебе компилятор от MS

Оть тебе GCC

int DLL_EXPORT IntTest(int X)
{
 return X / 3;
}

mov     ecx, [esp+arg_0]
mov     edx, 55555556h
mov     eax, ecx
imul    edx
sar     ecx, 1Fh
sub     edx, ecx
mov     eax, edx
retn

unsigned DLL_EXPORT DwordTest(unsigned X)
{
 return X /  3;
}

mov     edx, 0AAAAAAABh
mov     eax, [esp+arg_0]
mul     edx
shr     edx, 1
mov     eax, edx
retn


 
Rouse_ ©   (2013-07-29 15:27) [41]


> DevilDevil ©   (29.07.13 15:21) [40]

А смысл то какой? Дельфя всеравно все будет по старинке генерировать.


 
DevilDevil ©   (2013-07-29 15:31) [42]

> Rouse_ ©   (29.07.13 15:27) [41]

> А смысл то какой? Дельфя всеравно все будет по старинке
> генерировать.


нет, если заменять деление на умножение со сдвигом
или перейти на асм

тут суть не в том, чтобы обязательно применять в жизни. Суть в том, чтобы как минимум знать, как это можно применить на практике. Не всё же водопровод обсуждать, можно и программистскими задачками на этом форуме заняться


 
Rouse_ ©   (2013-07-29 16:14) [43]


> Суть в том, чтобы как минимум знать, как это можно применить
> на практике.

Практического применения не имеет, ну разве ты не соберешся свой собственный компилятор реализовать, а если просто заменить деление в коде - достаточно через asm вставку произвести изменения, раз уж так хочется.
Тратить-же рабочее время в пустую, пытаясь оптимизировать все и вся - есть очень плохой подход к разработке ПО.


 
DevilDevil ©   (2013-07-29 16:31) [44]

Ни в коем случае не нужно оптимизировать всё и вся. Очевидно, бывают задачи, в которых приходится обрабатывать большой объём данных и производить различные вычисления, такие как деление например. Приведённый мной код сводит 30 тактное деление к операциям в 6 (ну 7-8 максимум) тактов. Говорить, что оптимизировать деление в программах не нужно - достаточно опрометчиво; ибо операция распространённая и отжирает немеряно тактов.

> а если просто заменить деление в коде - достаточно через
> asm вставку произвести изменения, раз уж так хочется.


в [31] как раз и поднят этот вопрос. Ибо что именно писать в "asm вставку" общественности не известно :). Можно конечно скомпилировать хорошим С++ компилятором и посмотреть у него в дизассемблере... но как то это не по мастерски что ли. Имхо


 
Павиа   (2013-07-29 16:35) [45]

Во первых твои ответы легко гуглится.
Что касается замены сдвигов, то оно всем известно. И где надо используется.
Вот только там где надо это это от силы один случай из 1000.
Преждевременная оптимизация это зло в квадрате.


 
Inovet ©   (2013-07-29 16:37) [46]

Меня как-то занимал вопрос. Вот есть деление по модулю и целочисленное деление. Почему нет конструкции для получения результатов этих двух двух операций вместе, раз уж они одной процессорной инструкцией реализованы, да и не только инструкцией - по логике деления остаток всё равно где-то хранится.

Ну, нестандартая конструкция нужна, видимо, поэтому забили.


 
Inovet ©   (2013-07-29 16:40) [47]

> [44] DevilDevil ©   (29.07.13 16:31)
> общественности не известно :).

Ладно, смайлик стоит. Сочтём за юмор.


 
DevilDevil ©   (2013-07-29 16:42) [48]

> Павиа   (29.07.13 16:35) [45]
> Во первых твои ответы легко гуглится.


ну так скопируй из гугла да приведи сюда
чтобы эти "формулы" не имели погрешностей; чтобы результат деления эмулировался однозначно

> Что касается замены сдвигов, то оно всем известно. И где надо используется.
> Вот только там где надо это это от силы один случай из 1000.


действительно на практике деления на степень двойки редки
но о них никто здесь не говорит, если ты внимательно почитаешь

> Преждевременная оптимизация это зло в квадрате.
а современные конвейеры выполняют div за три такта
знаем, слышали :)


 
Павиа   (2013-07-29 16:43) [49]


> Почему нет конструкции для получения результатов этих двух
> двух операций вместе

Как это нету в модуле math лежит если не путаю divmod


 
DevilDevil ©   (2013-07-29 16:44) [50]

> Inovet ©   (29.07.13 16:37) [46]
> Меня как-то занимал вопрос. Вот есть деление по модулю и
> целочисленное деление. Почему нет конструкции для получения
> результатов этих двух двух операций вместе, раз уж они одной
> процессорной инструкцией реализованы, да и не только инструкцией
> - по логике деления остаток всё равно где-то хранится.


справку надо читать, а не курить
div возвращает и частное и остаток

> Inovet ©   (29.07.13 16:40) [47]
> Ладно, смайлик стоит. Сочтём за юмор.


даже не знаю как комментировать :)
может ты просто не понял вопрос ?


 
Павиа   (2013-07-29 16:48) [51]


> а современные конвейеры выполняют div за три тактазнаем,
>  слышали :)

Нет больше 9 но меньше 30.


> чтобы эти "формулы" не имели погрешностей; чтобы результат
> деления эмулировался однозначно

http://www.agner.org/optimize/optimizing_assembly.pdf
страница 140. В интернетах есть и русский перевод.


 
DevilDevil ©   (2013-07-29 16:49) [52]

> Павиа   (29.07.13 16:43) [49]

> Как это нету в модуле math лежит если не путаю divmod


да, но он убогий
возвращает только word-ы если не ошибаюсь
вообще мне кажется компилятор сам должен распознавать ситуации, когда в коде рядом располагаются и div и mod на одних и тех же операндах


 
Павиа   (2013-07-29 16:50) [53]


> даже не знаю как комментировать :)может ты просто не понял
> вопрос ?

Таки по поводу труб. Текут сегодня должны были почсенить. Но что-то мне не нравиться как счётчик поставили так течь начали. Уже 2 раз сантехника вызываем.


 
DevilDevil ©   (2013-07-29 17:00) [54]

> http://www.agner.org/optimize/optimizing_assembly.pdf
> страница 140. В интернетах есть и русский перевод.


неплохая отправная точка
когда я листал этот документ - увидел только оптимизации с плавающей точкой
но там муторно написано. используется shr вместо sar, imul вместо mul (для unsigned). Милости прошу - отвечай на вопрос [31]. Будем тестировать


 
Inovet ©   (2013-07-29 17:07) [55]

> [49] Павиа   (29.07.13 16:43)
> divmod

Не, то процедура ещё и асм, теряется весь смысл. Надо,чтобы по месту работало типа инлайн бы была.


 
Inovet ©   (2013-07-29 17:11) [56]

> [50] DevilDevil ©   (29.07.13 16:44)
> div возвращает и частное и остаток

Ты о каком div?


 
Inovet ©   (2013-07-29 17:11) [57]

> [50] DevilDevil ©   (29.07.13 16:44)
> может ты просто не понял вопрос ?

Всё я понял.


 
Inovet ©   (2013-07-29 17:13) [58]

Я вот как раз сейчас услышал один принцип:
"Принцип Боба. Когда у Боба есть проблемы со всеми, главной проблемой обычно является сам Боб."


 
DevilDevil ©   (2013-07-29 17:21) [59]

> Павиа
> Таки по поводу труб. Текут сегодня должны были почсенить.
>  Но что-то мне не нравиться как счётчик поставили так течь
> начали. Уже 2 раз сантехника вызываем.


> Нет больше 9 но меньше 30.

http://www.agner.org/optimize/instruction_tables.pdf

у Bobcat mul/imul от 1 до 4 тактов. div/idiv - от 27 до 81
Sandy Bridge: 1-2 и 11-84 соответственно

а в каком мануале ты видел, что меньше 30 ?
не в инструкции к счётчику воды случайно ?


 
DevilDevil ©   (2013-07-29 17:30) [60]

> Inovet ©   (29.07.13 17:07) [55]
>
> Не, то процедура ещё и асм, теряется весь смысл. Надо,чтобы
> по месту работало типа инлайн бы была.


Вот как это выглядит в цивилизованных компиляторах

int DLL_EXPORT DivModTest(int X, int Y)
{
   return (X / Y) + (X % Y);
}

mov     eax, [esp+arg_0]
cdq
idiv    [esp+arg_4]
add     eax, edx
retn


 
Sapersky   (2013-07-29 17:46) [61]

84 такта - это r64... вроде редко нужно делить что-то именно 64-битное.

DIVPS (SSE) - 10-14 тактов, 4 числа за раз, в расчёте на 1 число около 3 тактов.
RCPPS (приближённое 1/x) - 5 тактов, хотя мне в единственном случае применения точности как раз не хватило.

Понятно, что если алгоритм уже заточен под целые, то SSE с плавающей точкой вряд ли имеет смысл, конверсия форматов здорово испортит (а то и вообще убьёт) всё ускорение. С другой стороны, возможно, это повод пересмотреть формат данных:
http://blog.lexa.ru/2011/08/27/o_legacy_i_formatakh_dannykh.html


 
DevilDevil ©   (2013-07-29 17:52) [62]

> Sapersky   (29.07.13 17:46) [61]

ну так целочисленное деление не равно делению с плавающей точкой
в таком случае вообще делается UN_DIV_CONST = 1/const
вообще без операций деления

и зачем конвертировать alu <--> fpu, когда уже тысячу лет придумали оптимизированное целочисленное деление на константу. По сути ветка является только детализацией этого механизма, чтобы и мы при желании могли им воспользоваться в своих Delphi-проектах


 
Sapersky   (2013-07-29 19:41) [63]

А что тут детализировать...
Шаманский метод вроде [40] - это конечно замечательно, но если компилятор не поддерживает, нужно каждый раз высчитывать магические числа вручную, и ещё прикидывать какие-то исключения, с которыми это не будет работать.
Уж проще тогда написать UN_DIV_CONST = 65536 div const + умножение со сдвигом, а не хватит точности - пусть будет div, и пофиг сколько тактов.

А если переложить высчитывание магических чисел на комп, то это оправдывается, только если нужно делить массив чисел на одно и то же. У Агнера Фога [51] там дальше есть ссылка на его библиотечку с подобными функциями. Вот это в принципе имеет смысл, можно попробовать... правда делить массив на одно число нужно не так уж и часто.


 
Inovet ©   (2013-07-29 19:49) [64]

> [60] DevilDevil ©   (29.07.13 17:30)
> Вот как это выглядит в цивилизованных компиляторах
>
> int DLL_EXPORT DivModTest(int X, int Y)
> {
>   return (X / Y) + (X % Y);
> }
>
> mov     eax, [esp+arg_0]
> cdq
> idiv    [esp+arg_4]
> add     eax, edx
> retn


Вот как это выглядит в Builder XE4

struct tdiv_test
{
 int quot;
 int rem;
 tdiv_test(int q, int r) {quot = q; rem = r;}
};

inline __fastcall tdiv_test div_test(int num, int div)
{
 return tdiv_test(num / div, num & div);
}

int __fastcall DivModTest(int n, int d)
{
 return n / d + n % d;
}

int __fastcall DivModTest2(int n, int d)
{
 tdiv_test t = div_test(n, d);
 return t.quot + t.rem;
}

@DivModTest$qqrii segment virtual
align 2
@@DivModTest$qqrii proc near
?live16385@0:
@1:
push      ebx
push      esi
mov       ebx,edx
mov       ecx,eax
?live16385@16: ; ECX = n, EBX = d
mov       eax,ecx
cdq
idiv      ebx
mov       esi,eax
mov       eax,ecx
cdq
idiv      ebx
add       esi,edx
mov       eax,esi
?live16385@32: ;
@3:
@2:
pop       esi
pop       ebx
ret
@@DivModTest$qqrii endp
@DivModTest$qqrii ends
_TEXT ends

_TEXT segment dword public use32 "CODE"
@DivModTest2$qqrii segment virtual
align 2
@@DivModTest2$qqrii proc near
?live16386@0:
@4:
push      ebx
push      esi
add       esp,-8
?live16386@16: ; EAX = n, EDX = d
mov       ecx,eax
mov       ebx,edx
mov       esi,ebx
and       esi,ecx
mov       eax,ecx
cdq
idiv      ebx
mov       dword ptr [esp],eax
mov       eax,esi
mov       dword ptr [esp+4],eax
?live16386@32: ; EAX = @temp4
mov       edx,dword ptr [esp]
add       edx,eax
mov       eax,edx
?live16386@48: ;
@6:
@5:
pop       ecx
pop       edx
pop       esi
pop       ebx
ret
@@DivModTest2$qqrii endp
@DivModTest2$qqrii ends


 
Inovet ©   (2013-07-29 20:19) [65]

> [64] Inovet ©   (29.07.13 19:49)
> return tdiv_test(num / div, num & div);

А чёрт. Думаю - что за фигня, а оно вон что. Вот так правильно

 return tdiv_test(num / div, num % div);

_TEXT segment dword public use32 "CODE"
@DivModTest2$qqrii segment virtual
align 2
@@DivModTest2$qqrii proc near
?live16386@0:
@4:
push      ebx
push      esi
add       esp,-8
?live16386@16: ; EAX = n, EDX = d
mov       ecx,eax
mov       ebx,edx
mov       eax,ecx
cdq
idiv      ebx
mov       esi,edx
mov       eax,ecx
cdq
idiv      ebx
mov       dword ptr [esp],eax
mov       eax,esi
mov       dword ptr [esp+4],eax
?live16386@32: ; EAX = @temp4
mov       edx,dword ptr [esp]
add       edx,eax
mov       eax,edx
?live16386@48: ;
@6:
@5:
pop       ecx
pop       edx
pop       esi
pop       ebx
ret
@@DivModTest2$qqrii endp
@DivModTest2$qqrii ends
_TEXT ends

Короче, результат не радует. Это с дефолтными установками компилятора на Release, только отключена динамическая RTL, но здесь это пофиг.


 
Jeer ©   (2013-07-29 20:34) [66]

TC - "Очередной метр".


 
Inovet ©   (2013-07-29 20:39) [67]

> [66] Jeer ©   (29.07.13 20:34)
> "Очередной метр".

Я бы даже сказал - метроном.:)


 
DevilDevil ©   (2013-07-29 21:13) [68]

> Sapersky   (29.07.13 19:41) [63]

> Шаманский метод вроде [40] - это конечно замечательно, но
> если компилятор не поддерживает, нужно каждый раз высчитывать
> магические числа вручную


об том и речь

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


не
суть как раз в том, что если делать грамотно - он сработает для любых констант на любом диапазоне чисел (в рамках integer/cardinal конечно). А если подходить упрощено типа 2^16 - тогда естественно погрешность будет. Но зачем, если можно эмулировать деление один в один

> А если переложить высчитывание магических чисел на комп,
>  то это оправдывается, только если нужно делить массив чисел
> на одно и то же.


не мне тебе рассказывать, что такое такты. Если в твоём цикле 3 деления реализованы рассматриваемым способом - это уже 90+ тактов экономии на одну итерацию. Взять тот же IntToStr - много делений (в последних версиях на 100).

я не агитирую использовать такую оптимизацию везде )))
я агитирую разобраться в вопросе. ибо вопрос программистский, а мы тут вроде как метим в мастера


 
DevilDevil ©   (2013-07-29 21:15) [69]

> Inovet ©   (29.07.13 20:19) [65]

> Короче, результат не радует. Это с дефолтными установками
> компилятора на Release, только отключена динамическая RTL,
>  но здесь это пофиг.


ну что и требовалось доказать
не понятно только, зачем тебе целая структура для двух значений
используй Point )))


 
DevilDevil ©   (2013-07-29 21:16) [70]

Удалено модератором


 
Jeer ©   (2013-07-29 21:20) [71]

Удалено модератором


 
Jeer ©   (2013-07-29 21:22) [72]

Удалено модератором


 
DevilDevil ©   (2013-07-29 21:24) [73]

Удалено модератором


 
Rouse_ ©   (2013-07-29 23:10) [74]


> Jeer ©   (29.07.13 21:22) [72]

Сергей Батькович, ну ё мае...
Предвижу закрытие ветки...


 
Jeer ©   (2013-07-29 23:12) [75]

>Сергей Батькович

Не.. я затих:)
Пусть меряет дальше спокойно.


 
DevilDevil ©   (2013-07-29 23:16) [76]

> Rouse_ ©   (29.07.13 23:10) [74]
> Предвижу закрытие ветки...

сейчас то что не так ?


 
Jeer ©   (2013-07-29 23:45) [77]

>сейчас то что не так ?

Все нормально.
"Работа мозгом, нежели поршнями, дает больше шансов на повышение интеллекта"

P.S.
Я тоже доставал старших "глупостями", а потом - они становились в очередь за работой ко мне.


 
DevilDevil ©   (2013-07-30 00:32) [78]

> Jeer ©   (29.07.13 23:45) [77]

если ты хочешь чтобы я или иной участник форума тебя похвалил - тебе следует создать собственную ветку


 
DevilDevil ©   (2013-07-30 01:17) [79]

> Sapersky   (29.07.13 19:41) [63]
> У Агнера Фога [51] там дальше есть ссылка на его библиотечку
> с подобными функциями.


да, забавно
думаю взять их за основу, только адаптировать под привычный нам register, а не cdecl :)

смотрю исходники и не могу понять смысл действия
можешь объяснит, что происходит ?

 bsr     ecx, ebx               ; floor(log2(d-1))
 ...
 // ахтунг ахтунг
 inc     ecx                    ; L = ceil(log2(d))        
 sub     ecx, 1                 ; shift count = L - 1
 adc     ecx, 0                 ; avoid negative shift count


как ecx может получиться отрицательный - я чёт не понимаю


 
Sapersky   (2013-07-30 17:40) [80]

Прямо перед bsr строчка:
mov ecx, -1  ; value for bsr if ebx = 0 (assuming bsr leaves dest unchanged if src = 0)
то есть странное телодвижение inc ecx/sub ecx/adc ecx - исключительно для обработки этого случая, чтобы привести ecx к 0.
Хотя в функции предварительного расчёта можно было и явный тест/джамп воткнуть, но автор как настоящий хакер не может не выпендриться...


 
Jeer ©   (2013-07-30 18:00) [81]

>если ты хочешь

хочу, чтобы ты наконец ты обратил внимание на адептов числодробилок ( того же Агнера ), а не на себя самого Великого и Самолюбивого пацанчика.

P.S.
Что делать в этой ветке, буду решать я.


 
DevilDevil ©   (2013-07-30 18:19) [82]

> Sapersky   (30.07.13 17:40) [80]
> Прямо перед bsr строчка:


да, я вчера вспомнил об этом когда лёг спать
приведу вчерашние наработки в божеский вид и выложу здесь
получилось даже производительнее, чем у адепта числодробилок ))))


 
DevilDevil ©   (2013-07-30 18:20) [83]

> Jeer ©   (30.07.13 18:00) [81]

на смахивает на то, что внимание нужно тебе, а не адептам числодробилок
у них, в отличие от тебя, его предостаточно ))))))))))


 
DevilDevil ©   (2013-07-30 22:26) [84]

Собственно готова реализация, которую можно применить в жизни
И для integer, и для cardinal

1 функция рассчитывает магическую константу и сдвиг для делителя
2 функция - это пример, как имея магическую константу и сдвиг, быстро разделить целое число

Пользуйте кому нужно

// magic-константа и сдвиг для чисел со знаком
// Division должен быть больше 0
procedure extract_div_magic(const Divisor: integer; out Magic, Shift: integer); overload;
asm
 cmp eax, 1
 jg @norm
 je @1
 ud2 // d <= 0. generate error
@1:
 mov [edx], 1
 mov [ecx], 0
 ret
@norm:
 push edx

 dec eax          // d-1
 mov edx, ecx
 bsr ecx, eax     // floor(log2(d-1))
 mov [edx], ecx   // shift found
 mov edx, 1
 shl edx, cl
 lea ecx, [eax+1] // restore d
 xor eax, eax
 div ecx

 pop edx
 inc eax
 mov [edx], eax   // magic
end;

// пример как можно эмулировать целочисленное деление на константу
// (для чисел со знаком)
function integer_div_example(const X: integer; const Magic, Shift: integer): integer;
asm
 // делимое потом ещё понадобится
 // а свободных регистров пока нет
 push eax

 // edx := (int64(eax) * reg) shr 32;
 // в качестве операндов выступают делимое и magic-константа
 imul edx

 // возвращаем делимое (потому что не было свободных регистров)
 pop eax

 // корректировка результата (от edx)
 add edx, eax
 shr eax, 31
 sar edx, cl {в реальных условиях константа}
 add eax, edx
end;

// magic-константа и сдвиг для чисел без знака
procedure extract_div_magic(const Divisor: cardinal; out Magic, Shift: cardinal); overload;
asm
 cmp eax, 1
 jg @norm
 je @1
 ud2 // d = 0. generate error
@1:
 mov [edx], 1
 mov [ecx], 0
 ret
@norm:
 push ebx
 push ecx
 dec eax  
 push edx

 lea ebx, [eax+1]
 bsr ecx, eax
 mov edx, 1
 inc ecx
 xor eax, eax
 shl edx, cl
 cmp cl, 20h
 adc edx, -1
 sub edx, ebx
 div ebx
 pop edx
 inc  eax
 sub  ecx, 1
 mov  [edx], eax
 seta  al
 setae dl
 neg   al
 movzx edx, dl
 and   al,cl
 movzx eax, al
 pop   ecx
 shl   eax, 8
 pop   ebx
 or    eax, edx
 mov   [ecx], eax
end;

// пример как можно эмулировать целочисленное деление на константу
// (для чисел без знака)
function cardinal_div_example(const X: cardinal; const Magic, Shift: cardinal): cardinal;
asm
 // делимое потом ещё понадобится
 push eax

 // умножаем делимое на Magic --> результат в edx
 mul edx

 // возвращаем делимое
 pop eax

 // корректировка результата (от edx)
 sub eax, edx
 shr eax, cl {в реальных условиях константа}
 shr ecx, 8  // в реальных условиях не нужно
 add eax, edx
 shr eax, cl {в реальных условиях константа}
end;


Кроме очевидного профита при делении на константы, сии наработки можно использовать при множественном делении на одно и то же число. Возьмём например код
int MultiDivTest(int X1, int X2, int X3, int X4, int X5, int D)
{
 return (X1 / D) + (X2 / D) + (X3 / D) + (X4 / D) + (X5 / D);
}


Не знаю как ICC, но GCC честно выполняет 5 делений. Мы же в "аналогичных" ситуациях можем решить 1 делением и 5 умножениями.


 
DevilDevil ©   (2013-07-30 22:52) [85]

поправочка

*procedure extract_div_magic(const Divisor: cardinal; out Magic, Shift: cardinal); overload;
asm
 cmp eax, 1
 ja @norm


Хочу обговорить вот какой вопрос.
По аналогии с [40] ставим своей задачей реализацию деления на константу 3
Вызываем extract_div_magic(3, integer, integer), получаем магическую константу $AAAAAAAB, сдвиг - 1

Получается "функция" деления на 3 будет у нас выглядеть так:
function IntDiv3(const X: integer): integer;
asm
 mov edx, $AAAAAAAB
 mov ecx, eax
 imul edx

 add edx, ecx
 shr ecx, 31
 sar edx, 1
 lea eax, [ecx + edx]
end;


что меня беспокоит?
да хотя бы то, что компилятор GCC ([40]):
1) использует магическую константу $55555556
2) делает "доведение" в 2 команды, а не в 4 как у нас

и я бы возможно оправдал сей факт фразой "ну просто над GCC гении работают", если бы код в [31] тоже не выдавал константу $55555556. Со сдвигами только не понятно

остаётся вопрос. Что же не учёл Фог ?


 
Rouse_ ©   (2013-07-30 23:04) [86]

Через тесты прогонял?


 
DevilDevil ©   (2013-07-30 23:09) [87]

> Rouse_ ©   (30.07.13 23:04) [86]

да
попробуй оригинальные фоговские коды для чистоты эксперимента


 
DevilDevil ©   (2013-07-31 12:14) [88]

Подоспел заказанный мной перевод главы Фога о целочисленном делении
http://zalil.ru/34653415

Можете читать на русском


 
Sha ©   (2013-08-01 00:17) [89]

> DevilDevil ©   (30.07.13 22:52) [85]
> Что же не учёл Фог ?

Формул подобных может быть не одна.
Более того, слегка изменяя их можно улучшить диапазон значений делимого.
После выбора констант будет правильно прогнать тест по всему диапазону значений делимого.



> DevilDevil ©   (31.07.13 12:14) [88]
> Можете читать на русском

Глянул сразу на последнюю страницу.
В первой же строчке формула не верна.
Лучше все-таки оригинал.


 
DevilDevil ©   (2013-08-01 00:35) [90]

> Sha ©   (01.08.13 00:17) [89]

ты рассуждаешь как-то... "а может быть, а может и не быть"
подход должен 100% попадание в результат div/idiv
а для этого точно нужно понять принцип и позже применять его
даже если формул несколько формул
надо выбрать оптимальный вариант
(что в частности продемонстрировал GCC)


 
Sha ©   (2013-08-01 00:49) [91]

> DevilDevil ©   (01.08.13 00:35) [90]

Я не рассуждаю. Я знаю.


 
DevilDevil ©   (2013-08-01 00:57) [92]

> Sha ©   (01.08.13 00:49) [91]
> Я не рассуждаю. Я знаю.


Либо ты плохо знаешь, либо Фог и прочие разработчики ICC/GCC - дилетанты. Одно из двух


 
Sha ©   (2013-08-01 01:02) [93]

Есть еще вариант. Боюсь обидеть.


 
DevilDevil ©   (2013-08-01 01:09) [94]

> Sha ©   (01.08.13 01:02) [93]

меня вряд ли может обидеть человек, не понимающий, что такое точность


 
Sha ©   (2013-08-01 01:12) [95]

ты его тоже вряд ли сможешь обидеть


 
DevilDevil ©   (2013-08-01 02:25) [96]

Почитал ещё раз Фога

1) По формулам действительно получается как в [40] (unsigned) - т.е. одно умножение и один сдвиг. Для 3 (как и в GCC) получается константа $AAAAAAAB
2) Но для знаковых (signed) у Фога не написано !
3) В "Алгоритмических трюках для программистов" ("Hacker"s Delight") для чисел со знаком тоже много команд; элегантных решений типа [40] не обозначено. Кстати 1Fh - это 31.
4) Не ясно, почему в asm_lib Фога столько манипуляций для unsigned-чисел

Итог:
Сейчас надо понять, как GCC сделал деление со знаком


 
Rouse_ ©   (2013-08-01 19:41) [97]


> DevilDevil ©   (01.08.13 00:57) [92]
> > Sha ©   (01.08.13 00:49) [91]
> > Я не рассуждаю. Я знаю.
> Либо ты плохо знаешь, либо Фог и прочие разработчики ICC/GCC
> - дилетанты. Одно из двух

Сурово...


 
DevilDevil ©   (2013-08-02 00:37) [98]

Чёт я пока не догоняю с делением на отрицательные


 
Юрий Зотов ©   (2013-08-02 01:07) [99]

> DevilDevil ©   (01.08.13 00:57) [92]

> Либо ты плохо знаешь, либо Фог и прочие
> разработчики ICC/GCC - дилетанты. Одно из двух


Не из двух, а из трех. Ведь не исключено, что дилетант кто-то третий...

Как думаешь, кто бы это мог быть? Или ты такую возможность даже не рассматриваешь?

PS
Ты с работами Sha знаком? Если нет, то ознакомься и подумай.


 
Германн ©   (2013-08-02 03:11) [100]


> Ты с работами Sha знаком? Если нет, то ознакомься и подумай.

А вот тут и проблема. Как познакомиться с работами Шарахова? :)
Я с ними знаком, ты с ними знаком. Мы с тобой знакомые люди! (с) А.Райкин


 
DevilDevil ©   (2013-08-02 09:38) [101]

> Юрий Зотов ©   (02.08.13 01:07) [99]

Какое мне дело до работ Шарахова, если человек сомневается в алгоритмах, которые уже не первое десятилетие доказывают свою надёжность

Ладно бы его мнение было основано на опытах, проведённых по материалам, указанным в данной ветке. А то получается зашёл человек в первую попавшуюся ветку, ляпнул ахинею - а я должен её поддерживать. Нет

Вы можете сколько угодно уважать Sha. Меня в данном случае интересует топик. И очень показательно, что не смотря на наличие убер мастеров уровня вселенной, тему кроме меня никто не развивает



Страницы: 1 2 3 вся ветка

Текущий архив: 2014.01.19;
Скачать: CL | DM;

Наверх




Память: 0.78 MB
Время: 0.01 c
15-1375272074
Юрий Зотов
2013-07-31 16:01
2014.01.19
Забавно


15-1375031178
Иксик
2013-07-28 21:06
2014.01.19
Илья Сегалович


15-1375303030
KilkennyCat
2013-08-01 00:37
2014.01.19
Просьба. Сделать хорошо Virtual TreeView


1-1320565646
Remad
2011-11-06 10:47
2014.01.19
GETMEM.INC


15-1375216205
Юрий
2013-07-31 00:30
2014.01.19
С днем рождения ! 31 июля 2013 среда