![]() |
| ||
|
|
Новости |
Поиск |
FAQ |
Статьи |
Книги |
Новости VCL
| | Форумы ЧАТ |
Орешник |
|
| |||
| |||
« предыдущая ветвь | форум | следующая ветвь »
Ускорение работы с динамическими массивами
alertus © (2005-05-24 16:40:00)Добрый день, подскажите, пожалуйста, что делать.
Я написал довольно сложный алгоритм (моделирование и обучение нейронных сетей). Все восхитительно работает, но медленно :((
Написал несколько классов для работы с матрицами, нейронными слоями и нейронными сетями, там все построено на динамических массивов real.
Я делаю это так:
var
a:array of array of real; //матрица
const
w = 10; //ширина
h = 5; //высота
.....
Initialize(a);
SetLength(a,w*h);
i:=2; //столбец
j:=4; //строка
a[j+(i-1)*w]:=0.5; //установим елемент
Finalize(a);
Ну и т.п. все это забито в методы, например
TMatrix.SetElem(i,j:integer;r:real);
Я знаю, что с динамическими массивами можно еще работать так:
type
myarray = array[1..1000] of real;
pmyarray = ^myarray;
var
a:pmyarray;
const
w = 10;
h = 5;
.....
i:=2;
j:=4;
getmem(a,w*h*sizeof(real));
a^[j+(i-1)*w]:=0.5;
freemem(a);
Но переписывать все очень долго и не знаю, будет ли выигрыш в скорости.
Может быть вообще надо отказаться от объектно-орентированной концепции, будет не так красиво, но быстро (под вопросом).
Подскажите, пожалуйста, кто сталкивался с подобными вопросами.
Может быть вообще скорость теряется не на динамических массивах, а на многочисленных арифметический операциях, как тогда ускорить их, я уже подумал о
asm
end;
alertus (2005-05-25 19:22:00) [21]Я сравниваю скорость по этому фрагменту:
edit1.text:=timetostr(time);
for i:=1 to 1000000000 do begin
for j:=0 to 9 do begin
c[j]:=a[j]*b[j];
end;
end;
edit2.text:=timetostr(time);
И он отличается минимум в 2 раза.
Mx (2005-05-25 19:17:00) [22]
> alertus (25.05.05 19:02) [53]
Насчет примера: Initialize вызывать не надо. Если ты сравниваешь скорость всей ButtonXClick, то возможно ее съедает SetLength, но доступ по скорости не может отличаться.
> Кстати, если я правильно понял, то и в окне CPU видно, что
> программа обращается к memo1.Lines.Count каждый раз
Естественно! Каждый раз в цикле по I, но не по J.
alertus (2005-05-25 19:16:00) [23]Да, не могу не согласиться, тут я неправ.
Но если присутствуют именно такие вложенные циклы, то второй код выгоднее.
begin...end (2005-05-25 19:13:00) [24]> alertus (25.05.05 19:02) [53]
> for i:=1 to 1000000 do begin
> for j:=1 to memo1.Lines.Count do begin
> end;
> end;
Здесь на каждом проходе цикла по i выполняется вход в цикл по j. Поэтому совершенно нормально и логично, что на каждой итерации цикла по iвычисляется конечное значение счётчика цикла по j. Это не опровергает вышесказанного, т.к. на каждой итерации цикла по jобращения к Lines.Count не происходит.
Измените пример так:
for i:=1 to memo1.Lines.Count do begin
for j:=1 to 1000000 do begin
end;
end
и удивитесь результату.
alertus (2005-05-25 19:08:00) [25]
> Код проверки можно увидеть?
Для динамического:
procedure TForm1.Button1Click(Sender: TObject);
var
a:array of real;
b:array of real;
c:array of real;
i,j:integer;
begin
Initialize(a);
Initialize(b);
Initialize(c);
SetLength(a,10);
SetLength(b,10);
SetLength(c,10);
for i:=0 to 9 do begin
a[i]:=i;
b[i]:=9-i;
end;
edit1.text:=timetostr(time);
for i:=1 to 1000000000 do begin
for j:=0 to 9 do begin
c[j]:=a[j]*b[j];
end;
end;
edit2.text:=timetostr(time);
finalize(a);
finalize(b);
finalize(c);
end;
Для статического:
procedure TForm1.Button2Click(Sender: TObject);
var
a:array[0..9] of real;
b:array[0..9] of real;
c:array[0..9] of real;
i,j:integer;
begin
for i:=0 to 9 do begin
a[i]:=i;
b[i]:=9-i;
end;
edit1.text:=timetostr(time);
for i:=1 to 1000000000 do begin
for j:=0 to 9 do begin
c[j]:=a[j]*b[j];
end;
end;
edit2.text:=timetostr(time);
end;
alertus (2005-05-25 19:02:00) [26]Может я чего-то не понимаю, но такой код:
edit1.Text:=timetostr(time);
f:=memo1.Lines.Count;
for i:=1 to 1000000 do begin
for j:=1 to f do begin
end;
end;
edit2.Text:=timetostr(time);
работает намного быстрее, чем
edit1.Text:=timetostr(time);
for i:=1 to 1000000 do begin
for j:=1 to memo1.Lines.Count do begin
end;
end;
edit2.Text:=timetostr(time);
Кстати, если я правильно понял, то и в окне CPU видно, что программа обращается к memo1.Lines.Count каждый раз.
begin...end (2005-05-25 19:02:00) [27]> alertus (25.05.05 19:00) [51]
Код проверки можно увидеть?
alertus (2005-05-25 19:00:00) [28]
> Повторю: если речь идёт о скорости доступа к элементу, то
> такого не может быть.
А какое может быть??
Я проверял так:
беру три масива (сначала динамических, а потом статических),
пробегаю по ним циклом, записывая в третий произведения первого и второго (поэлементно), результат -- в три раза быстрее...
begin...end (2005-05-25 18:57:00) [29]> Mx © (25.05.05 18:25) [48]
> Насчет скорости при выделении, думаю лучше использовать
> то, что сама Borland рекомендует (SetLength), а то вдруг
> че-нить поменяют?
Я и не предлагаю использовать GetMem для динамических массивов. Я говорил про использование GetMem для типов-указателей на статический массив. И имел в виду, что выделение одного и того же объёма памяти для соответствующихтипов этими двумя способами сильно различается по скорости.
> alertus (25.05.05 18:47) [49]
> Кстати, статитические массивы примерно в 3 раза быстрее
> работают, сегодня проверял.
Повторю: если речь идёт о скорости доступа к элементу, то такого не может быть.
alertus (2005-05-25 18:47:00) [30]
> Var - там ссылка в любом случае. Однако все зависит от задачи.
> Если в процедуре ты не планируешь изменение размера массива,
> то, в моем понимании, лучше через const.
Нет, я изменяю только элементы массива.
Вообще все массивы создаются один раз и больше не изменяются, динамические же они потому, что это универсальный Unit, который поддержывает сети с произвольным количеством слоев, нейронов и т.д.
Так что можно сказать, что массивы статические для данной программы, но для Unit'a они динамические...
Кстати, статитические массивы примерно в 3 раза быстрее работают, сегодня проверял.
Мне же надо разогнать в 5-6 раз (именно во столько раз медленнее работает, чем аналогичные программы в Matlab)
> А что ты делаещь с нейронной сетью. Оч оптимальное решение
> - упростить её.
В том то и дело, что заранее не известно, что будет делать сеть, она универсальная...
Mx (2005-05-25 18:25:00) [31]
> begin...end ©
В тот раз обсуждали именно доступ, про него и написал. Насчет скорости при выделении, думаю лучше использовать то, что сама Borland рекомендует (SetLength), а то вдруг че-нить поменяют?
> alertus
> Все таки интересно, как лучше написать
Без разницы. В любом случае Lines.Count вычислится только один раз. Теоритечески даже медленее второй вариант, так как включает присвоение переменной.
> поверил на слово
Как недавно выснилось, не всему из того, что там написано надо верить.
> как быстрее - var, const или без всего
Var - там ссылка в любом случае. Однако все зависит от задачи. Если в процедуре ты не планируешь изменение размера массива, то, в моем понимании, лучше через const.
Kolan (2005-05-25 16:50:00) [32]А что ты делаещь с нейронной сетью. Оч оптимальное решение - упростить её.
TUser (2005-05-25 16:23:00) [33]С динамическими массивами лучше работать примерно так
var A: array of integer;
ACount: integer;
// добавление элемента
if ACount = length(A) then
SetLength(A, ACount+256);
A[ACount]:=Value;
inc (ACount);
И еще - если есть возмолжность замени все на целочисленную арифметику. Эти два примеа позволят разогнаться раза в 2-5.
evvcom (2005-05-25 16:15:00) [34]
> И почему же, позволь полюбопытствовать?
Согласен. Вспомнил какую-то статью, не проверив. Без разницы. Компилятор единожды вычисляет .lines.count – 1, а потом только dec(i) выполняет.
begin...end (2005-05-25 16:05:00) [35]> alertus (25.05.05 12:42) [42]
> Мне не особо важна скорость выдеоления памяти, а вот скорость
> доступа к элементам -- важна.
Про скорость доступа я уже говорил. Вряд ли удастся её увеличить. Единственное, что можно делать -- это следить за оптимальностью вычисления самих индексов (в смысле, не вычислять их без необходимости).
> я прочитал таки статью
Таки зачем?
> evvcom © (25.05.05 15:54) [43]
> lin := .lines.count – 1;
> for i:=0 to lin do...
> быстрее
И почему же, позволь полюбопытствовать?
evvcom (2005-05-25 15:54:00) [36]Как быстрее можно понять из CPU Window. Var и Const - одинаково, а без всего - в зависимости от длины данных: до 4 байт включительно - так же, более 4-х дольше, и чем длиннее, тем дольше, так как делается копия данных в стеке.
lin := .lines.count – 1;
for i:=0 to lin do...
быстрее
alertus (2005-05-25 12:42:00) [37]Мне не особо важна скорость выдеоления памяти, а вот скорость доступа к элементам -- важна.
А как насчет передачи параметров в функцию, как быстрее - var, const или без всего (value)??
А еще вопрос такой: я прочитал таки статью http://www.delphimaster.ru/articles/optimization.html
и поверил на слово.
Все таки интересно, как лучше написать:
for i:=0 to memo1.lines.count – 1 do...
или
lin := .lines.count – 1;
for i:=0 to lin do...
Циклов у меня очень много, большой вложенности, я пытался смотреть в окно CPU, но понял мало...
begin...end (2005-05-25 11:15:00) [38]> Mx © (25.05.05 10:58) [40]
Нет, выделение памяти через GetMem как раз намного быстрее выделения такого же объёма через SetLength.
А вот скорость доступа к элементу массива будет практически такой же, о чём я и сказал в [1].
Mx (2005-05-25 10:58:00) [39]Если ты имеешь ввиду разницу в скорости работы через GetMem или SetLenght, то это уже обсуждалось, разницы никакой.
alertus (2005-05-25 10:36:00) [40]А какой вывод из этого всего можно сделать, как быстрее работать будет.
Я так понимаю, динамический массив всегда по ссылке передается, потому что передается не сам массив данных, а только адрес первого элемента и функция уже производит действия с елементами, расположенными по смещению от этого адреса:
a^[17]:=3.14;