Top.Mail.Ru
Мастера DELPHI, Delphi programming community Титульная страница Поиск, карта сайта Написать письмо 
| Новости | Поиск | FAQ | Статьи | Книги | Новости VCL |
| Форумы ЧАТ | Орешник | KOL и MCK |
Форумы -» Основная («Начинающим»)/ Базы / WinAPI / Компоненты / Сети / Media / Игры / Corba и COM / KOL / FreePascal / .Net / Прочее / RSDN.ORG

« предыдущая ветвь | форум | следующая ветвь »

Можно ли ускорить функцию? [D7, XP]


Страницы:   1 2 3

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
но это даже если и даст какое-то ускорение, то уж очень мизерное.
Подскажите, что еще тут можно оптимизировать?


Sha   (2016-05-30 15:08:00) [21]

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


SergP   (2016-05-30 15:01:00) [22]

Дерево это как?
Была мысль искать с помощью бинарного поиска, но я посчитал что затраты в подержании массива в отсортированном виде (или в организации поддержке дополнительного массива для индексов) этого не будут стоить.


Sha   (2016-05-30 14:21:00) [23]

> Правда в процедуре, из которой вызывается эта функция есть поиск по массиву

Наверно, дерево подошло бы больше.


SergP   (2016-05-30 12:27:00) [24]


> SergP ©   (30.05.16 12:24) [27]


Ошибся пока рисовал поля.. Вот так оно было:

-----------------------------
! 2 !   !   !   ! M1! 1 ! 0 !
-----------------------------
!   !   !   !   ! 3 ! 1 ! 0 !
-----------------------------
!   !   !   !   ! 4 ! 0 ! 0 !
-----------------------------
! 2 !   !   !   ! 5 ! 5 ! 3 !
-----------------------------
!   !   !   !   ! M2!   !   !
-----------------------------
!   !   !   !   !   ! 6 ! 3 !
-----------------------------
!   !   !   ! 4 !   ! 1 ! 0 !
-----------------------------


-----------------------------
! 2 !   ! # ! # ! M1! 1 ! 0 !
-----------------------------
!   !   ! # ! M1! 3 ! 1 ! 0 !
-----------------------------
!   !   ! # ! M1! 4 ! 0 ! 0 !
-----------------------------
! 2 !   ! # ! M2! 5 ! 5 ! 3 !
-----------------------------
!   !   ! # ! # ! M2! # ! M3!
-----------------------------
!   !   !   !   !   ! 6 ! 3 !
-----------------------------
! 1 !   !   ! 4 !   ! 1 ! 0 !
-----------------------------


SergP   (2016-05-30 12:24:00) [25]

Вот например:
-----------------------------
! 2 !   !   !   ! M1! 1 ! 0 !
-----------------------------
!   !   !   !   ! 3 ! 1 ! 0 !
-----------------------------
!   !   !   !   ! 4 ! 0 ! 0 !
-----------------------------
! 2 !   !   !   ! 5 ! 5 ! 3 !
-----------------------------
!   !   !   !   ! M2!   !   !
-----------------------------
!   !   !   !   !   ! 6 ! 3 !
-----------------------------
!   !   !   ! 4 !   ! 2 ! 0 !
-----------------------------


Кроме двух мин (одинарной и двойной) больше ничего не найдено. Тыкаем в нижний левый угол. там 1

-----------------------------
! 2 !   ! # ! # ! M1! 1 ! 0 !
-----------------------------
!   !   ! # ! M1! 3 ! 1 ! 0 !
-----------------------------
!   !   ! # ! M1! 4 ! 0 ! 0 !
-----------------------------
! 2 !   ! # ! M2! 5 ! 5 ! 3 !
-----------------------------
!   !   ! # ! # ! M2! # ! M3!
-----------------------------
!   !   !   !   !   ! 6 ! 3 !
-----------------------------
! 1 !   !   ! 4 !   ! 2 ! 0 !
-----------------------------


и программа за доли секунды (для нее это довольно легкий вариант) находит еще 4 мины и 8 ячеек где мин не может быть...
А вот сколько нужно вычислений произвести чтобы это найти перебором? Что-то мне такое не кажется реальным.


SergP   (2016-05-30 11:57:00) [26]


> Sha ©   (30.05.16 11:52) [24]
>
> Мне кажется, что проще отталкиваться непосредственно от
> условий [17],


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


SergP   (2016-05-30 11:52:00) [27]


> Sha ©   (27.05.16 22:24) [19]


Проверил. Правда не стал добавлять в запись поле cells, а сделал вычисление его по прежнему методу  SmallCells:=bitcount(SmallSource.field).count; дабы пока не менять остальной код. Т.е. в таком виде:
function GetEntranceSha(var BigSource, SmallSource: Twork): Twork;
var
 i, i1, i2, j, j1, j2, cells, Smallcells, BigMask, SmallMask: integer;
 BigPermit, SmallPermit: TMset;
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;
 SmallCells:=bitcount(SmallSource.field).count;
 for i:=0 to 2 do begin;
   BigMask:=1;
   for i1:=0 to 4 do begin;
     for i2:=0 to 5 do begin;
       if BigSource.MSet[i] and BigMask<>0 then for j:=0 to i do begin;
         for j1:=0 to i1 do begin;
           cells:=SmallCells-j-j1;
           if cells>i2 then cells:=i2;
           SmallMask:=1 shl (j1*6);
           for j2:=0 to cells do begin;
             if SmallSource.MSet[j] and SmallMask<>0 then begin;
               BigPermit[i]:=BigPermit[i] or BigMask;
               SmallPermit[j]:=SmallPermit[j] or SmallMask;
               Result.MSet[i-j]:=Result.MSet[i-j] or 1 shl ((i1-j1)*6+i2-j2);
               end;
             SmallMask:=SmallMask+SmallMask;
             end;
           end;
         end;
       BigMask:=BigMask+BigMask;
       end;
     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;


Вроде работает правильно.

Вобщем при обработке не особо тяжелого примера, но такого, где все-таки приходится ждать заметное время, получилось что с моим первоначальным вариантом расчет идет 44 секудны, с твоим 34 секунды...

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

var
workarr:packed array of TWork;
...
// Обработка вложений
// Разность двух групп добавляется в конец массива
// Если аналогичная группа уже имеется в массиве, то она корректируется
procedure Entrance(Start:integer);
var
 i,j,t,z:integer;
 EntSec:Twork;
 flag:boolean;
begin
 Application.ProcessMessages;
 t:=Workcount-1;
 for i:=0 to t do
 begin
   for j:=start to t do
     if (i<>j) and ((workarr[i].field and workarr[j].field) = workarr[i].field) and (workarr[i].field<>0) then  // i входит в j
     begin
       EntSec:=GetEntrance(workarr[j], workarr[i]);
       flag:=true;
       for z:=0 to workcount-1 do // if (z<>i) and (z<>j) then
          if workarr[z].field=EntSec.field then
       begin
         workarr[z].MSet[0]:=workarr[z].MSet[0] and EntSec.MSet[0];
         workarr[z].MSet[1]:=workarr[z].MSet[1] and EntSec.MSet[1];
         workarr[z].MSet[2]:=workarr[z].MSet[2] and EntSec.MSet[2];
         flag:=false;
         break;
       end;
       if flag then
       begin
         workarr[Workcount]:=EntSec;
         incworkcount;
       end;
     end;
   end;
end;


Sha   (2016-05-30 11:52:00) [28]

Мне кажется, что проще отталкиваться непосредственно от условий [17],
т.е. искать ячейки "где мин точно не может быть" перебором,
просто пробуя экономно расположить имеющиеся мины в ячейках.


SergP   (2016-05-30 09:45:00) [29]

Мда. в [21] я что-то не то сморозил... Почему-то раньше мне казалось что там хватит регистров.


SergP   (2016-05-29 20:57:00) [30]


> Вот и хочу найти нечто подобное для x86


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


SergP   (2016-05-29 20:47:00) [31]


> Sha ©   (27.05.16 22:17) [18]


Завтра попробую, сейчас Дельфи под рукой нет.
Хотя что-то склоняюсь к тому чтобы переписать функцию на ассемблере.

Пока грубо оно мне видится так: 5 регистров используем как указатели на BigSource, SmallSource, BigPermit, SmallPermit, Result.
Один регистр для всех четырех счетчиков цикла, по байту на каждый. 2 байта из 4 нам всегда доступны, а когда понадобятся другие то можно повертеть регистр с помощью ROR, ROL, BSWAP
Переменные BigMask, SmallMask вообще упразднить,вместо них использовать BTC, BTS
2 оставшихся регистра вроде должно хватить для вычислений и хранения промежуточных данных.
Кстати, мысль с использованием не 4, а 6 циклов тож постараюсь проработать.
Хотя есть вопрос: насколько медленно выполнение DIV ? В частности, если его использовать для деления на 8-разрядное число.


> dmk ©   (28.05.16 16:15) [20]
>
> >Вот только где бы по асму информации раздобыть нормальной?
>
> В источнике ;)


Конечно, лучше было бы на русском языке. Хотя в инете и на русском языке находил много чего. Но оно все не такое, что быстро запоминается...
Например, лет 25 назад, когда я интересовался ассемблером 8080 и Z80, тоже литература разная была. Одной можно было пользоваться, вроде все в ней есть, но как-то не то. А однажды попалась такая, что несколько раз прочитавши я уже знал на память все команды, все допустимые наборы операндов для каждой команды, время выполнения каждой команды в тактах в зависимости от операндов, флагов и пр, и даже запомнил коды многих команд, хотя и не ставил перед собой такой цели. (что тоже оказалось полезным, ибо позволяло видеть что делается в каком-нить участке программы, взглянувши на ее шестнадцатеричный дамп).
Вот и хочу найти нечто подобное для x86

Ну и по встроенному ассемблеру в Дельфи тоже есть вопросы:
Например передача параметров и возвращение результатов функций,
использование переменных (т.е. памяти где можно хранить промежуточные данные).
Какие расширения доступны в ассемблере в Дельфи 7, (типа ММХ и пр.)
ну и т.д.


dmk   (2016-05-28 16:15:00) [32]

>Вот только где бы по асму информации раздобыть нормальной?
В источнике ;)
http://www.intel.com/content/dam/www/public/us/en/documents/manuals/64-ia-32-architectures-software-developer-instruction-set-reference-manual-325383.pdf

Функции можно сделать переменными. Не поддерживает - используешь одну, поддерживает - другую.


Sha   (2016-05-27 22:24:00) [33]

Увидел пару опечаток:
           SmallMask:=1 shl (j1*6);
...
               Result.MSet[i-j]:=Result.MSet[i-j] or 1 shl ((i1-j1)*6+i2-j2);


Может еще что-нибудь найдется. Код не отлаживал.


Sha   (2016-05-27 22:17:00) [34]

Тупо переписал, чтобы убрать деление и выход за границу параллелепипеда 3х5х6.
Но не думаю, что будет заметное ускорение.

type
 TMSet = packed array[0..2] of integer;
 TWork = packed record
  Field: int64;
  Cells: integer;
  MSet: TMset;
  Probe: Extended;
  end;

function GetEntrance(var BigSource, SmallSource: Twork): Twork;
var
 i, i1, i2, j, j1, j2, k, cells, BigMask, SmallMask: integer;
 BigPermit, SmallPermit: TMset;
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;
 Result.Cells:=BigSource.Cells-SmallSource.Cells; // êîëè÷åñòâî ÿ÷ååê â ðàçíîñòíîé ãðóïïå
 for i:=0 to 2 do begin;
   BigMask:=1;
   for i1:=0 to 4 do begin;
     for i2:=0 to 5 do begin;
       if BigSource.MSet[i] and BigMask0 then for j:=0 to i do begin;
         for j1:=0 to i1 do begin;
           cells:=SmallSource.Cells-j-j1;
           if cells>i2 then cells:=i2;
           SmallMask:=1 shl (j1*5);
           for j2:=0 to cells do begin;
             if SmallSource.MSet[j] and SmallMask0 then begin;
               BigPermit[i]:=BigPermit[i] or BigMask;
               SmallPermit[j]:=SmallPermit[j] or SmallMask;
               Result.MSet[i-j]:=Result.MSet[i-j] or 1 shl ((i1-j1)*5+i2-j2);
               end;
             SmallMask:=SmallMask+SmallMask;
             end;
           end;
         end;
       BigMask:=BigMask+BigMask;
       end;
     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;


SergP   (2016-05-27 17:47:00) [35]


> Sha ©   (27.05.16 16:46) [16]
>
> Про функцию вроде все более-менее понятно.
> Осталось понять, что нужно в итоге после 25 минут вычислений
> получить.


программа должна на поле 7*7 с 11 минами, при наличии одной или нескольких открытых ячеек:
1. Найти ячейки с минами, которые можно найти. Найти закрытые ячейки, где мин точно не может быть (если таковые существуют), т.е. те, куда можно далее тыкать...


Sha   (2016-05-27 16:46:00) [36]

Про функцию вроде все более-менее понятно.
Осталось понять, что нужно в итоге после 25 минут вычислений получить.


SergP   (2016-05-27 16:31:00) [37]


> Sha ©   (27.05.16 16:14) [13]
>
> Так какую задачу все-таки решаем?


В смысле "какую"?
Сейчас наиболее узкое место программы - приведенная в сабже функция, в плане времени выполнения...
Вобще-то я старался делать чтобы расчеты были мгновенными, чтобы производить их в процессе модификации исходных данных... И оно почти так и работает, где вычисляется все за доли секунды, но встречаются тяжелые ситуации, когда программа задумывается минут на 20-25. вот это и хочу ускорить...

Или Вы имеете ввиду что нужно сменить всю логику вычислений?
Я уже писал, что опробовал несколько вариантов и в конце-концов мне в голову пришел такой. Больше ничего лучшего в голову не приходило...
Но если у Вас имеется хорошая мысль - то думаю что ее тоже можно рассмотреть.


SergP   (2016-05-27 16:21:00) [38]

Одна группа может состоять из нескольких вариантов.
группа, в нее входящая также может состоять из нескольких вариантов.
И разница может содержать в себе несколько вариантов.

Например если большая группа может состоять из вариантов:
1+1+1+1,  1+1+2, 1+3, 2+2
а группа в нее входящаяможет содержать варианты:
1+1, 2
то в результате работы функции получим разнцу: 1+1, 2
и кроме того из большой группы убираем вариант 1+3

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


Sha   (2016-05-27 16:14:00) [39]

Так какую задачу все-таки решаем?


SergP   (2016-05-27 16:00:00) [40]


> Sha ©   (27.05.16 15:26) [11]
>
> > SergP ©   (27.05.16 15:00) [9]
>
> Почему нельзя вариант кодировать его номером,
> а все эти соответствующие ему количества мин и т.п. хранить
> в таблице?


так вариантов может несколько.
данная функция работает "множествами" вариантов.


Страницы:   1 2 3

Наверх

Титульная страница Поиск, карта сайта Написать письмо