![]() |
| ||
|
|
Новости |
Поиск |
FAQ |
Статьи |
Книги |
Новости VCL
| | Форумы ЧАТ |
Орешник |
|
| |||
| |||
« предыдущая ветвь | форум | следующая ветвь »
Можно ли ускорить функцию? [D7, XP]
SergP © (2016-05-26 19:16:00)Написал функцию:
...
type TMSet = packed array[0..2] of integer;
type TWork=packed record
field:int64;
MSet:TMset;
Probe:Extended;
end;
...
function GetEntrance(var BigSource,SmallSource: Twork):Twork;
var
i,i2,j,j2,k,cells:byte;
BigPermit,SmallPermit:TMset;
BigMask,SmallMask:integer;
begin
for i:=0 to 2 do
begin
BigPermit[i]:=0;
SmallPermit[i]:=0;
result.MSet[i]:=0;
end;
Result.field:=BigSource.field xor SmallSource.field;
cells:=bitcount(Result.field).count; // количество ячеек в разностной группе
for i:=0 to 2 do
begin
BigMask:=1;
for i2:=0 to 29 do
begin
if (BigSource.MSet[i] and BigMask) = BigMask then for j:=0 to i do
begin
SmallMask:=1;
for j2:=0 to i2 do
begin
if ((SmallSource.MSet[j] and SmallMask)=SmallMask) and ((i2 mod 6) >= (j2 mod 6)) and (i-j+((i2-j2) div 6)+((i2-j2) mod 6)<=cells) then
begin
k:=i-j;
BigPermit[i]:=BigPermit[i] or BigMask;
SmallPermit[j]:=SmallPermit[j] or SmallMask;
Result.MSet[k]:=Result.MSet[k] or (1 shl (i2-j2));
end;
SmallMask:=SmallMask shl 1;
end;
end;
BigMask:=BigMask shl 1;
end;
end;
BigSource.MSet:=BigPermit;
SmallSource.MSet:=SmallPermit;
if ((Bigsource.MSet[0] or Bigsource.MSet[1] or Bigsource.MSet[2])=0)
or ((Smallsource.MSet[0] or SmallSource.MSet[1] or SmallSource.MSet[2])=0)
or ((Result.MSet[0] or Result.MSet[1] or Result.MSet[2])=0) then raise Exception.Create('Error GetEntrance');
end;
Если нужно описание что делает функция, то суть такова:
У нас имеются 3 разновидности неких сущностей, назовем их A, B, С
Их количество ограничено. Сущностей типа A может быть от 0 до 2, B - от 0 до 4, C - от 0 до 5
Имеются некие ячейки. В ячейке может быть размещена либо одна из сущностей, либо ячейка может быть пустой.
Несколько ячеек назовем группой.
Имеются 2 группы, назовем большая и маленькая. маленькая является частью большой.
TMset, т.е. (mset[2],mset[1],mset[0]) описывает множество комбинаций сушностей и пустых ячеек в группе, всего для этого нужно 90 бит
у меня используются 3 числа типа integer, у каждого из которых используется по 30 бит.
Например (1,0,0) означает что в группе имеются 2 штуки A
(0,0,1) означает что группа пустая
(2,128,16384) означает что группа может состоять из 2А+С, либо A+B+C, либо 2*B+2*C
и т.д.
Суть думаю ясна.
Нужно найти множество комбинаций сушностей для новой группы, которая равна разности большой и маленькой групп,
с корректированием исходных данных (отбросить заведомо неприемлимые комбинации в исходных данных)
Если в результате образуется (0,0,0) в любой из групп (большой, маленькой, разностной), то генерируем исключение,
ибо это означает на ошибку в исходных данных, и далее продолжать вычисления безсмысленно.
В процессе расчетов функция вызывается сотни тысяч раз, возможно и более миллиона. Хотелось бы, чтобы она работала побыстрее.
Оформить это все в виде таблицы понятно, что нереально.
Использовать какие-то промежуточные таблицы реальных размеров - пока не могу придумать как.
Пока думал внутренний цикл поменять например с
for j2:=0 to i2 do
на
for j2:=i2 downto 0 do
но это даже если и даст какое-то ускорение, то уж очень мизерное.
Подскажите, что еще тут можно оптимизировать?
SergP (2016-06-22 15:49:00) [1]А. ну там еще табличные данные есть
const
GentArr:packed array[0..29,0..15] of integer=(
(0,0,0,0,$00000001,$00000043,$000010C7,$000431CF,$010C73DF,$031CF7FF,$073DFFFF,$ 0F7FFFFF,$1FFFFFFF,$3FFFFFFF,$3FFFFFFF,$3FFFFFFF),
(0,0,0,0,$00000002,$00000086,$0000218E,$0008639E,$0218E7BE,$0639EFBE,$0E7BEFBE,$ 1EFBEFBE,$3EFBEFBE,$3EFBEFBE,$3EFBEFBE,$3EFBEFBE),
(0,0,0,0,$00000004,$0000010C,$0000431C,$0010C73C,$0431CF3C,$0C73CF3C,$1CF3CF3C,$ 3CF3CF3C,$3CF3CF3C,$3CF3CF3C,$3CF3CF3C,$3CF3CF3C),
(0,0,0,0,$00000008,$00000218,$00008638,$00218E38,$08638E38,$18E38E38,$38E38E38,$ 38E38E38,$38E38E38,$38E38E38,$38E38E38,$38E38E38),
(0,0,0,0,$00000010,$00000430,$00010C30,$00430C30,$10C30C30,$30C30C30,$30C30C30,$ 30C30C30,$30C30C30,$30C30C30,$30C30C30,$30C30C30),
(0,0,0,0,$00000020,$00000820,$00020820,$00820820,$20820820,$20820820,$20820820,$ 20820820,$20820820,$20820820,$20820820,$20820820),
(0,0,0,0,$00000040,$000010C0,$000431C0,$010C73C0,$031CF7C0,$073DFFC0,$0F7FFFC0,$ 1FFFFFC0,$3FFFFFC0,$3FFFFFC0,$3FFFFFC0,$3FFFFFC0),
(0,0,0,0,$00000080,$00002180,$00086380,$0218E780,$0639EF80,$0E7BEF80,$1EFBEF80,$ 3EFBEF80,$3EFBEF80,$3EFBEF80,$3EFBEF80,$3EFBEF80),
(0,0,0,0,$00000100,$00004300,$0010C700,$0431CF00,$0C73CF00,$1CF3CF00,$3CF3CF00,$ 3CF3CF00,$3CF3CF00,$3CF3CF00,$3CF3CF00,$3CF3CF00),
(0,0,0,0,$00000200,$00008600,$00218E00,$08638E00,$18E38E00,$38E38E00,$38E38E00,$ 38E38E00,$38E38E00,$38E38E00,$38E38E00,$38E38E00),
(0,0,0,0,$00000400,$00010C00,$00430C00,$10C30C00,$30C30C00,$30C30C00,$30C30C00,$ 30C30C00,$30C30C00,$30C30C00,$30C30C00,$30C30C00),
(0,0,0,0,$00000800,$00020800,$00820800,$20820800,$20820800,$20820800,$20820800,$ 20820800,$20820800,$20820800,$20820800,$20820800),
(0,0,0,0,$00001000,$00043000,$010C7000,$031CF000,$073DF000,$0F7FF000,$1FFFF000,$ 3FFFF000,$3FFFF000,$3FFFF000,$3FFFF000,$3FFFF000),
(0,0,0,0,$00002000,$00086000,$0218E000,$0639E000,$0E7BE000,$1EFBE000,$3EFBE000,$ 3EFBE000,$3EFBE000,$3EFBE000,$3EFBE000,$3EFBE000),
(0,0,0,0,$00004000,$0010C000,$0431C000,$0C73C000,$1CF3C000,$3CF3C000,$3CF3C000,$ 3CF3C000,$3CF3C000,$3CF3C000,$3CF3C000,$3CF3C000),
(0,0,0,0,$00008000,$00218000,$08638000,$18E38000,$38E38000,$38E38000,$38E38000,$ 38E38000,$38E38000,$38E38000,$38E38000,$38E38000),
(0,0,0,0,$00010000,$00430000,$10C30000,$30C30000,$30C30000,$30C30000,$30C30000,$ 30C30000,$30C30000,$30C30000,$30C30000,$30C30000),
(0,0,0,0,$00020000,$00820000,$20820000,$20820000,$20820000,$20820000,$20820000,$ 20820000,$20820000,$20820000,$20820000,$20820000),
(0,0,0,0,$00040000,$010C0000,$031C0000,$073C0000,$0F7C0000,$1FFC0000,$3FFC0000,$ 3FFC0000,$3FFC0000,$3FFC0000,$3FFC0000,$3FFC0000),
(0,0,0,0,$00080000,$02180000,$06380000,$0E780000,$1EF80000,$3EF80000,$3EF80000,$ 3EF80000,$3EF80000,$3EF80000,$3EF80000,$3EF80000),
(0,0,0,0,$00100000,$04300000,$0C700000,$1CF00000,$3CF00000,$3CF00000,$3CF00000,$ 3CF00000,$3CF00000,$3CF00000,$3CF00000,$3CF00000),
(0,0,0,0,$00200000,$08600000,$18E00000,$38E00000,$38E00000,$38E00000,$38E00000,$ 38E00000,$38E00000,$38E00000,$38E00000,$38E00000),
(0,0,0,0,$00400000,$10C00000,$30C00000,$30C00000,$30C00000,$30C00000,$30C00000,$ 30C00000,$30C00000,$30C00000,$30C00000,$30C00000),
(0,0,0,0,$00800000,$20800000,$20800000,$20800000,$20800000,$20800000,$20800000,$ 20800000,$20800000,$20800000,$20800000,$20800000),
(0,0,0,0,$01000000,$03000000,$07000000,$0F000000,$1F000000,$3F000000,$3F000000,$ 3F000000,$3F000000,$3F000000,$3F000000,$3F000000),
(0,0,0,0,$02000000,$06000000,$0E000000,$1E000000,$3E000000,$3E000000,$3E000000,$ 3E000000,$3E000000,$3E000000,$3E000000,$3E000000),
(0,0,0,0,$04000000,$0C000000,$1C000000,$3C000000,$3C000000,$3C000000,$3C000000,$ 3C000000,$3C000000,$3C000000,$3C000000,$3C000000),
(0,0,0,0,$08000000,$18000000,$38000000,$38000000,$38000000,$38000000,$38000000,$ 38000000,$38000000,$38000000,$38000000,$38000000),
(0,0,0,0,$10000000,$30000000,$30000000,$30000000,$30000000,$30000000,$30000000,$ 30000000,$30000000,$30000000,$30000000,$30000000),
(0,0,0,0,$20000000,$20000000,$20000000,$20000000,$20000000,$20000000,$20000000,$ 20000000,$20000000,$20000000,$20000000,$20000000));
SergP (2016-06-22 15:48:00) [2]И то правда... Как-то сразу не подумал об этом.
В результате получилась такая функция: (не знаю насколько сама функция теперь быстрее работает чем вариант Sha, но вся программа тестовый пример посчитала за 1м 44с вместо 4м 42с)
function GetEntrance(var BigSource, SmallSource: Twork): Twork;
procedure ErrorRaiseException;
begin
raise Exception.Create('Error GetEntrance');
end;
asm
push ebx
push esi
push edi
push ebp
xor ebp,ebp
mov ebx,[edx]
xor ebx,[eax]
mov [ecx],ebx
jz @nobit0
@cntbit0: lea esi,[ebx-1]
inc ebp
and ebx,esi
jnz @cntbit0
@nobit0: mov ebx,[edx+$04]
xor ebx,[eax+$04]
mov [ecx+$04],ebx
jz @nobit1
@cntbit1: lea esi,[ebx-1]
inc ebp
and ebx,esi
jnz @cntbit1
@nobit1:
lea edi,[ecx+$08]
lea esi,[edx+$08]
mov edx,[eax+$10]
push edx
mov edx,[eax+$0C]
push edx
mov edx,[eax+$08]
push edx
xor edx,edx
push edx
push edx
push edx
push eax
mov [ecx+$08],edx
mov [ecx+$0C],edx
mov [ecx+$10],edx
mov eax,ebp
cmp eax,11 // êîë-âî ÿ÷ååê áîëåå 11 âëèÿåò òàê æå êàê 11
js @next
mov eax,11
@next: add eax,4
xchg edx,[esi]
@bgns0a: bsf ecx,edx
jz @ns0ex
@bgns0b: btr edx,ecx
lea ebp,[ecx*8]
lea ebp,[eax+ebp*2]
lea ebp,[ebp*4+GentArr]
@_ns0b0: mov ebx,[ebp]
and ebx,[esp+$10]
jz @ns0b1
bts [esi],ecx
or [esp+$04],ebx
shr ebx,cl
or [edi],ebx
@ns0b1: mov ebx,[ebp-$04]
and ebx,[esp+$14]
jz @ns0b2
bts [esi],ecx
or [esp+$08],ebx
shr ebx,cl
or [edi+$04],ebx
@ns0b2: mov ebx,[ebp-$08]
and ebx,[esp+$18]
jz @bgns0a
bts [esi],ecx
or [esp+$0C],ebx
shr ebx,cl
or [edi+$08],ebx
bsf ecx,edx
jnz @bgns0b
@ns0ex: xor edx,edx
xchg edx,[esi+$04]
@bgns1a: bsf ecx,edx
jz @ns1ex
@bgns1b: btr edx,ecx
lea ebp,[ecx*8]
lea ebp,[eax+ebp*2]
lea ebp,[ebp*4+GentArr]
@_ns1b1: mov ebx,[ebp]
and ebx,[esp+$14]
jz @ns1b2
bts [esi+$04],ecx
or [esp+$08],ebx
shr ebx,cl
or [edi],ebx
@ns1b2: mov ebx,[ebp-$04]
and ebx,[esp+$18]
jz @bgns1a
bts [esi+$04],ecx
or [esp+$0C],ebx
shr ebx,cl
or [edi+$04],ebx
bsf ecx,edx
jnz @bgns1b
@ns1ex: xor edx,edx
xchg edx,[esi+$08]
@bgns2a: bsf ecx,edx
jz @ns2ex
@bgns2b: btr edx,ecx
lea ebp,[ecx*8]
lea ebp,[eax+ebp*2]
lea ebp,[ebp*4+GentArr]
@_ns2b2: mov ebx,[ebp]
and ebx,[esp+$18]
jz @bgns2a
bts [esi+$08],ecx
or [esp+$0C],ebx
shr ebx,cl
or [edi],ebx
bsf ecx,edx
jnz @bgns2b
@ns2ex:
pop eax
pop edx
mov [eax+$08],edx
pop edx
mov [eax+$0C],edx
pop edx
mov [eax+$10],edx
add esp,12 // пропускаем 3 числа засунутые в стек, которые нам уже не нужны
mov eax,[edi]
or eax,[edi+$04]
or eax,[edi+$08]
jnz @noraise
call ErrorRaiseException
@noraise: pop ebp
pop edi
pop esi
pop ebx
end;
Теоретически возможности для дальнейшей оптимизации должны иметься. Например более правильно использовать регистры, может где можно добиться спаривания. Хотя думаю что на этом можно и остановиться.
Rouse_ (2016-06-21 19:34:00) [3]да сделай отдельную процедуру куда напиши
raise Exception.Create('Error GetEntrance');
и вызывай ее - че мучаться то?
SergP (2016-06-21 18:48:00) [4]а как быть с этим?
> :00461C6D A1146E4000 mov eax, dword ptr [00406E14]
Rouse_ (2016-06-21 11:44:00) [5]
call Exception.Create
call @RaiseExcept
SergP (2016-06-21 08:31:00) [6]Почти переписал все на ассемблере, значительно изменивши (счетчиков циклов там уже нет, осталось только 3 последовательных (не вложенных) цикла где не используются счетчики, ну и удалось задействовать табличные данные для удобства и ускорения (размером 2 кб). Не тестировал еще, так как находясь в больнице заниматься этим проблематично, в основном думать приходится используя тетрадь и ручку.
Только вот возник вопрос: Как вот эту часть там оформить?
... raise Exception.Create('Error GetEntrance');
?
В дизасемблированном виде оно выглядит так (без строки: "Error GetEntrance")
:00461C66 B98C1C4600 mov ecx, 00461C8C
:00461C6B B201 mov dl, 01
:00461C6D A1146E4000 mov eax, dword ptr [00406E14]
:00461C72 E82D98FAFF call 0040B4A4
:00461C77 E8741CFAFF call 004038F0
Первая строчка это собственно загрузка адреса строки "Error GetEntrance"
что означают вторая и третяя строчки. А также где мне взять адреса єтих двух подпрограмм?
SergP (2016-06-01 19:00:00) [7]
> SergP © (01.06.16 18:56) [44]
>
> Вот думаю, может попробовать представить это все в виде
> системы линейных уравнений, и решать по принципу метода
> Гаусса?...
Хотя так тоже свои проблемы будут. Например как учесть то условие, что количество переменных уравнения имеющих значение 3 может быть не более двух, 2 не более четырех и 1 не более пяти?
SergP (2016-06-01 18:56:00) [8]Вот думаю, может попробовать представить это все в виде системы линейных уравнений, и решать по принципу метода Гаусса?...
Rouse_ (2016-06-01 14:41:00) [9]угу, вечерком гляну
SergP (2016-06-01 14:32:00) [10]
> Rouse_ © (31.05.16 20:55) [40]
>
> Эммм... дык там же вообще практически на коленке пишется
> без извращений с оптимизацией...
>
> Выложи исходники, где там у тебя 2 часа думает и ТЗ само.
>
отправил на мыло, которое тут на сайте указано.
SergP (2016-05-31 22:25:00) [11]
> Rouse_ © (31.05.16 20:55) [40]
>
> Эммм... дык там же вообще практически на коленке пишется
> без извращений с оптимизацией...
>
Хм. ты так уверен?
> Выложи исходники, где там у тебя 2 часа думает и ТЗ само.
На домашнем компе щас нет Дельфи. А исходники нужно еще привести в более-менее нормальный вид, и снабдить нормальными коментариями, ибо разобраться будет трудно. Завтра приведу в более-менее пригодный вид и выложу, желательно на мыло. просто довольно большой, 1.5 тысячи строк. Да еще и тестовые примеры нужно прицепить...
А насчет 2 часов - уже конечно во много раз быстрее, но тем не менее памяти жрет порядочно. Пока бывали ситуации когда по 60 мб под массивы сжирало.
ЗЫ Я поначалу думал что просто будет, перепробовал несколько вариантов представления данных, но остановился на последнем.
Когда писал программу для обычного сапера - то там все расчеты производились моментально, в процессе модификации исходных данных, т.е. даже не нужна была отдельная кнопка для запуска расчета, даже при тех данных, когда найденная в интернете подобная программа для обычного сапера, (которая в большинстве случаев тоже довольно быстро все просчитывала) была мной поставлена в тупик одним из вариантов исходных данных, когда она сказала что ждать конца расчетов осталось около 72 тыс. часов, т.е. около 9 лет.
Rouse_ (2016-05-31 20:55:00) [12]Эммм... дык там же вообще практически на коленке пишется без извращений с оптимизацией...
Выложи исходники, где там у тебя 2 часа думает и ТЗ само.
SergP (2016-05-31 20:30:00) [13]Т.е. для обычного сапера там все намного проще было. давно написал. А это для такого себе "хитрого" сапера, с минами разного калибра...
SergP (2016-05-31 20:29:00) [14]
> Rouse_ © (31.05.16 18:57) [37]
>
> Ты в итоге чего пишешь то?
> ИИ для сапера?
типа того...
Rouse_ (2016-05-31 18:57:00) [15]Ты в итоге чего пишешь то?
ИИ для сапера?
SergP (2016-05-31 18:41:00) [16]М-да. Попался сложный пример, над которым программа думала около 2-х часов.
Вот только что прилепил бинарный поиск по массиву:
Если найдено такое значение, то производятся операции как и раньше (т.е. модификация имеющейся записи), если не найдено, то новая запись вставляется в массив так, чтобы массив поддерживался в отсортированном по возрастанию виде по полю field (int64), со сдвигом хвоста массива с помощью move.
Думал что это мало что даст из-за сдвигов.
Но протестил на том же примере, на котором программа работала более 2-х часов, и теперь она это посчитала на глаз где-то за минуты 1,5-2
)))
Sha (2016-05-30 20:09:00) [17]Еще, вероятно проверки перед циклами вроде
if BigSource.MSet[i]<>0 then for i1:=0 to 4 do begin;
дадут небольшое ускорение
Sha (2016-05-30 19:54:00) [18]> переписал в отдельную функцию поиск по массиву на ассемблере
хорошо увидеть результат, вдруг там еще что осталось )
> даже не имею понятия как ускорить поиск
Я имел в виду, что, например, в потомке можно хранить индекс родительского множества.
В этом случае два вложенных цикла (по родителям и потомкам с проверкой на родство)
сведутся в один цикл по потомкам с известным родителем.
SergP (2016-05-30 18:34:00) [19]
> Sha © (30.05.16 15:08) [31]
>
> При вычитании подмножества из множества всегда получается
> подмножество.
> Это можно учесть в коде, чтобы проще находить подмножества.
>
так поиск идет все равно по полю field. в некотором роде это тоже множество. Но это множество ячеек группы, а не мин
Честно говоря, даже не имею понятия как ускорить поиск, учитывая Ваше высказывания. Даже не представляю себе, что Вы имели ввиду.
SergP (2016-05-30 18:20:00) [20]На данный момент выделил и переписал в отдельную функцию поиск по массиву на ассемблере (Тут Rouse_ подсказал http://www.delphimaster.ru/cgi-bin/forum.pl?id=1464608311&n=3)
В результате учитывая вышеизложенное и также плюс Sha © (27.05.16 22:17) [18]
вместе оно дало уже ощутимый прирост.
На примере придуманному от балды, первоначальный код давал время расчетов 44 секунды, а с учетом обоих модификаций кода уже 20 секунд.
т.е. более чем в 2 раза.
И это я еще не пытался переписывать сабжевую функцию на ассемблере...