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

Вниз

Ускорение работы с динамическими массивами   Найти похожие ветки 

 
alertus   (2005-05-24 16:40) [0]

Добрый день, подскажите, пожалуйста, что делать.
Я написал довольно сложный алгоритм (моделирование и обучение нейронных сетей). Все восхитительно работает, но медленно :((

Написал несколько классов для работы с матрицами, нейронными слоями и нейронными сетями, там все построено на динамических массивов 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;


 
begin...end ©   (2005-05-24 16:43) [1]

> alertus   (24.05.05 16:40)

> Я знаю, что с динамическими массивами можно еще работать
> так:
> ...
> не знаю, будет ли выигрыш в скорости

Скорость операции доступа к элементу массива не увеличится.


 
Alx2 ©   (2005-05-24 16:44) [2]

Сразу: избегай real. Замени на Single или Double.


 
Alx2 ©   (2005-05-24 16:50) [3]

Также передачу параметров в процедуры/функции лучше делать по адресу (например, procedure name(const Value : Double)).

Вообще, используй какой-нибудь профилер для поиска узких мест. Рекомендую VTune.


 
alertus   (2005-05-24 16:50) [4]

2Alx2 уже пробовал, скорость не меняется, а с single точность намного меньше (0.1+0.2=0.300000011920929), а насчет double: как написано в help"е Delphi, это real=double, если не стоит

{$REALCOMPATIBILITY ON}

Если же поставить эту диррективу, то real=real48 (на скорости тоже почти не сказывается).

PS
скорость я тестирую так:
a:=0.7648654;
b:=0.79645784;
for i:=1 to 100000 do
 for j:=1 to 100000 do
   c:=a*b;
label1.caption:="!!!";


 
Alx2 ©   (2005-05-24 16:52) [5]

>alertus   (24.05.05 16:50) [4]

> это real=double, если не стоит
>{$REALCOMPATIBILITY ON}

Сорри. Действительно.

Тогда нужно профилировать.


 
evvcom ©   (2005-05-24 16:57) [6]

А зачем array of array, если потом все равно работаешь как с одномерным?


 
begin...end ©   (2005-05-24 17:00) [7]

> Alx2 ©   (24.05.05 16:50) [3]

> Также передачу параметров в процедуры/функции лучше делать
> по адресу (например, procedure name(const Value : Double)).

const -- это НЕ передача по адресу.


 
alertus   (2005-05-24 17:02) [8]

Я работаю не как с одномерным, я написал процедуры типа
TMatrix.GetElem(i,j:integer).
А вообще я просто не понял, как обращаться по другому к двумерным массивам, если я обращусь a[2,3]:=10 то возникает вопрос, а какова ширина матрици??
Ведь до этого мы указали только количество элементов, например:
Initialize(a,25)


 
alertus   (2005-05-24 17:03) [9]

Я и так передаю по адресу, я же не передаю функции весь массив, а адрес первого элемента, в этом и есть идеалогия динамических массивов.


 
Alx2 ©   (2005-05-24 17:03) [10]

>begin...end ©   (24.05.05 17:00) [7]
A constant (const) parameter is like a local constant or read-only variable. Constant parameters are similar to value parameters, except that you can’t assign a value to a constant parameter within the body of a procedure or function, nor can you pass one as a var parameter to another routine. (But when you pass an object reference as a constant parameter, you can still modify the object’s properties.)
Using const allows the compiler to optimize code for structured- and string-type parameters. It also provides a safeguard against unintentionally passing a parameter by reference to another routine .

Here, for example, is the header for the CompareStr function in the SysUtils unit:

function CompareStr(const S1, S2: string): Integer;

Because S1 and S2 are not modified in the body of CompareStr, they can be declared as constant parameters.


 
Alx2 ©   (2005-05-24 17:05) [11]

>alertus   (24.05.05 17:03)

Без кода будет только телепатия :)
Высылай, если-что.


 
alertus   (2005-05-24 17:09) [12]

А что такое "профилер"??


 
begin...end ©   (2005-05-24 17:11) [13]

> Alx2 ©   (24.05.05 17:03) [10]

> It also provides a safeguard against unintentionally passing
> a parameter by reference to another routine .

Ну, и?


 
Alx2 ©   (2005-05-24 17:12) [14]

>alertus   (24.05.05 17:09) [12]

profiler - инструмент для построения "профиля" программы.
Профиль, если говорить карикатурно, - график "скорострельности" различных участков программы.


 
Alx2 ©   (2005-05-24 17:12) [15]

>begin...end ©   (24.05.05 17:11)
Я угадывать должен, что ты имеешь в виду?


 
begin...end ©   (2005-05-24 17:19) [16]

> Alx2 ©   (24.05.05 17:12) [15]

> It also provides a safeguard against unintentionally passing
> a parameter by reference to another routine .

Перевожу: "Это также обеспечивает защиту против непреднамеренной передачи параметра по ссылке в другую подпрограмму".

Теперь ответьте на вопрос: следует ли из этой фразы, что параметр с модификатором const передаётся по адресу, как Вы заявили в [3]?


 
Mx ©   (2005-05-24 17:26) [17]


> begin...end ©

Как я понял, const - это по адресу, но компилятор запрещает явное изменение ее значения. Скажем в передав по const массив record"ов мы сможем изменить значение его переменных, но не сможем изменить значение самой ссылки на массив, т.к. она const. Можно, кстати, проверить адреса - они совпадут.


> It also provides a safeguard against unintentionally passing
> a parameter by reference to another routine

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


 
Mx ©   (2005-05-24 17:30) [18]

Гм... правда здесь возникает другой вопрос: как по const передаются свойства? Видимо все гораздо хитрее...


 
begin...end ©   (2005-05-24 17:31) [19]

> Mx ©   (24.05.05 17:26) [17]

> Как я понял, const - это по адресу ...

Читаем процитированный выше хелп:

> Constant parameters are similar to value parameters, except
> that you can’t assign a value to a constant parameter within
> the body of a procedure or function, nor can you pass one
> as a var parameter to another routine.

и переводим:

"Константные параметры похожи на параметры по значению, однако вы не можете присваивать значение константному параметру в теле процедуры или функции, и вы не можете передавать его как параметр по ссылке в другую подпрограмму."

Откуда следует, что const -- это передача по ссылке?

Да и в окне CPU всё видно, вообще-то.


 
begin...end ©   (2005-05-24 17:32) [20]

> Mx ©   (24.05.05 17:30) [18]

Какие свойства?


 
Alx2 ©   (2005-05-24 17:37) [21]

>begin...end ©   (24.05.05 17:19)

> Alx2 ©   (24.05.05 17:12) [15]

> It also provides a safeguard against unintentionally passing
> a parameter by reference to another routine .

>Перевожу: "Это также обеспечивает защиту против
>непреднамеренной передачи параметра по ссылке в другую
>подпрограмму".

>Теперь ответьте на вопрос: следует ли из этой фразы, что
>параметр с модификатором const передаётся по адресу, как Вы
>заявили в [3]?

Сорри, с выделенным фрагментом наврал. Конечно, именно из этой фразы не следует.

Но параметр передается по ссылке.

>Да и в окне CPU всё видно, вообще-то.

Вот-вот. Там и видно.


 
Mx ©   (2005-05-24 17:39) [22]


> begin...end ©

В качестве var"а свойство объекта мы не передадим, а через const пожалуйста. Это я имел ввиду.

Сейчас проверил: если передать record без const, то адреса - разные, а если через const - одинаковые.


> похожи на параметры по значению

Похожи не значат, что именно так.


 
alertus   (2005-05-24 17:42) [23]

Раз уж речь зашла.
Я так понимаю, var - это передача по ссылке, а const - нет, значит по значению

> It also provides a safeguard against unintentionally passing
> a parameter by reference to another routine.

То есть если программист не хочет нечаянно передать функции параметр по ссылке, чтобы та его не изменила ненароком, он пишет const...


 
Alx2 ©   (2005-05-24 17:42) [24]

> begin...end ©
Возможно, будет важным дополнением: если параметр целиком помещается в 4 байта, то передается по значению. Если нет - по ссылке.


 
begin...end ©   (2005-05-24 17:49) [25]

> Alx2 ©   (24.05.05 17:37) [21]

function Func1(Value: Integer): Integer;
begin
 Result := Value
end;

function Func2(var Value: Integer): Integer;
begin
 Result := Value
end;

function Func3(const Value: Integer): Integer;
begin
 Result := Value
end;

var
 I: Integer;
begin
 I := 1;
 Func1(I);
 Func2(I);
 Func3(I)
end.


>> Да и в окне CPU всё видно, вообще-то.
> Вот-вот. Там и видно.

Не знаю, что видно у Вас, а у меня видно следующее:

mov [esp],$00000001

mov eax,[esp]
call Func1

mov eax,esp
call Func2

mov eax,[esp]
call Func3


> Alx2 ©   (24.05.05 17:42) [24]

Замените в приведённом коде Integer на Extended. Что-то принципиально поменяется?


 
Mx ©   (2005-05-24 17:50) [26]


> Alx2 ©

Возможно. А как быть с параметрами типа Extended? Свойство, возвращаемое по функции тоже можно передать как Const, ссылки не будет, но значение больше 4 байт.


 
Alx2 ©   (2005-05-24 17:55) [27]

>begin...end ©   (24.05.05 17:49)

Сыплю пепел на голову. Спасибо за развенчивание моего мифа относительно const. Черт, а ведь был твердо уверен :)
Пошел учить матчасть.


 
begin...end ©   (2005-05-24 17:55) [28]

> Mx ©   (24.05.05 17:39) [22]

> В качестве var"а свойство объекта мы не передадим, а через
> const пожалуйста.

Конечно, не передадите. Свойство -- это ведь не переменная. И у него нет адреса. А вот значение -- есть.


 
Alx2 ©   (2005-05-24 18:12) [29]

И все-таки не пойму. Зачем нужно const extended передавать по значению? Только лишние траты. Чего-то я не учитываю...


 
malkolinge ©   (2005-05-24 18:34) [30]

VarArrayLock()


 
Mx ©   (2005-05-24 21:03) [31]


> begin...end ©
> Конечно, не передадите

Спасибо, конечно, но ты почему-то совсем не то видишь в моих постах. Я понимаю, что свойство - не переменная и написал так с той самой целью, что const не обязательно ссылка, потому как на свойство ссылку не передать.

Так как быть с record"ом? В случае передачи по const адрес переменной один и тот же, а в случае без const - разный.


 
Mx ©   (2005-05-24 21:15) [32]

Совсем интересно, может кто разъяснит? Судя по CPU View во всех трех случаях, после замены типа формального параметра на простенький record с двумя Integer"ами, в EAX помещается ESP, однако оператор @ показывает в случае Func1 все-равно возвращает число, отличное от Func2/Func3:


type
 TM = record
   S: Integer;
   B: Integer;
 end;

function Func1(Value: TM): Integer;
begin
Result := Value.S;
end;

function Func2(var Value: TM): Integer;
begin
Result := Value.S;
end;

function Func3(const Value: TM): Integer;
begin
Result := Value.S;
end;

procedure TForm1.FormCreate(Sender: TObject);
var
I: TM;
begin
I.S := 1;
I.B := 2;
Func1(I);
Func2(I);
Func3(I)
end;


в CPU View:

MOV  [ESP],$1
MOV  [ESP+$4],$2

MOV  EAX,ESP
CALL FUNC1

MOV  EAX,ESP
CALL FUNC2

MOV  EAX,ESP
CALL FUNC3


 
VMcL ©   (2005-05-24 21:41) [33]

>>Alx2 ©   (24.05.05 16:50) [3]

"const" имеет особый смысл в плане оптимизации для объектов с управляемым временем жизни (long strings, dynamic arrays, interfaces) и объектов структурного (record) или массивного (array) типа.


 
Mx ©   (2005-05-24 21:45) [34]

Я тока не понял почему в EAX все разы записывается ESP, но "собака"Value в Func1 возвращает адрес отличный от TForm1.Create, Func2, Func3 - в них он идентичен.


 
GuAV ©   (2005-05-25 00:25) [35]

Mx ©   (24.05.05 21:45) [34]

Записи, которые не помещающиеся в маш слово, всегда передаются по ссылке. Для таких записей переданных без const/var/out при необходимости создаётся рабочая копия ( "собака"Value возвращает именно её адрес).

см [33]


 
Defunct ©   (2005-05-25 00:35) [36]

Mx ©   (24.05.05 21:45) [34]

Вы в курсе что такое ESP? Если нет, тогда [35] трудно будет совместить с вашим CPU View из [32].


 
GuAV ©   (2005-05-25 01:07) [37]

к [35]

Разумеется, рабочая копия создаётся кодом вызываемой функции.

Это верно для соглашений register и pascal.
для cdecl или stdcall (safecall) запись переданная без const/var/out педедаётся по значению.


 
Mx ©   (2005-05-25 02:19) [38]


> Defunct ©

В курсе в курсе...


> GuAV ©
> Разумеется, рабочая копия создаётся кодом вызываемой функции.

Теперь понятно.


 
alertus   (2005-05-25 10:36) [39]

А какой вывод из этого всего можно сделать, как быстрее работать будет.
Я так понимаю, динамический массив всегда по ссылке передается, потому что передается не сам массив данных, а только адрес первого элемента и функция уже производит действия с елементами, расположенными по смещению от этого адреса:
a^[17]:=3.14;


 
Mx ©   (2005-05-25 10:58) [40]

Если ты имеешь ввиду разницу в скорости работы через GetMem или SetLenght, то это уже обсуждалось, разницы никакой.


 
begin...end ©   (2005-05-25 11:15) [41]

> Mx ©   (25.05.05 10:58) [40]

Нет, выделение памяти через GetMem как раз намного быстрее выделения такого же объёма через SetLength.
А вот скорость доступа к элементу массива будет практически такой же, о чём я и сказал в [1].


 
alertus   (2005-05-25 12:42) [42]

Мне не особо важна скорость выдеоления памяти, а вот скорость доступа к элементам -- важна.
А как насчет передачи параметров в функцию, как быстрее - 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, но понял мало...


 
evvcom ©   (2005-05-25 15:54) [43]

Как быстрее можно понять из CPU Window. Var и Const - одинаково, а без всего - в зависимости от длины данных: до 4 байт включительно - так же, более 4-х дольше, и чем длиннее, тем дольше, так как делается копия данных в стеке.

lin := .lines.count – 1;
for i:=0 to lin do...

быстрее


 
begin...end ©   (2005-05-25 16:05) [44]

> 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 16:15) [45]


> И почему же, позволь полюбопытствовать?

Согласен. Вспомнил какую-то статью, не проверив. Без разницы. Компилятор единожды вычисляет .lines.count – 1, а потом только dec(i) выполняет.


 
TUser ©   (2005-05-25 16:23) [46]

С динамическими массивами лучше работать примерно так
var A: array of integer;
   ACount: integer;

// добавление элемента
if ACount = length(A) then
 SetLength(A, ACount+256);
A[ACount]:=Value;
inc (ACount);

И еще - если есть возмолжность замени все на целочисленную арифметику. Эти два примеа позволят разогнаться раза в 2-5.


 
Kolan ©   (2005-05-25 16:50) [47]

А что ты делаещь с нейронной сетью. Оч оптимальное решение - упростить её.


 
Mx ©   (2005-05-25 18:25) [48]


> begin...end ©

В тот раз обсуждали именно доступ, про него и написал. Насчет скорости при выделении, думаю лучше использовать то, что сама Borland рекомендует (SetLength), а то вдруг че-нить поменяют?


> alertus
> Все таки интересно, как лучше написать

Без разницы. В любом случае Lines.Count вычислится только один раз. Теоритечески даже медленее второй вариант, так как включает присвоение переменной.


> поверил на слово

Как недавно выснилось, не всему из того, что там написано надо верить.


> как быстрее - var, const или без всего

Var - там ссылка в любом случае. Однако все зависит от задачи. Если в процедуре ты не планируешь изменение размера массива, то, в моем понимании, лучше через const.


 
alertus   (2005-05-25 18:47) [49]


> Var - там ссылка в любом случае. Однако все зависит от задачи.
> Если в процедуре ты не планируешь изменение размера массива,
> то, в моем понимании, лучше через const.

Нет, я изменяю только элементы массива.
Вообще все массивы создаются один раз и больше не изменяются, динамические же они потому, что это универсальный Unit, который поддержывает сети с произвольным количеством слоев, нейронов и т.д.
Так что можно сказать, что массивы статические для данной программы, но для Unit"a они динамические...
Кстати, статитические массивы примерно в 3 раза быстрее работают, сегодня проверял.
Мне же надо разогнать в 5-6 раз (именно во столько раз медленнее работает, чем аналогичные программы в Matlab)


> А что ты делаещь с нейронной сетью. Оч оптимальное решение
> - упростить её.

В том то и дело, что заранее не известно, что будет делать сеть, она универсальная...


 
begin...end ©   (2005-05-25 18:57) [50]

> Mx ©   (25.05.05 18:25) [48]

> Насчет скорости при выделении, думаю лучше использовать
> то, что сама Borland рекомендует (SetLength), а то вдруг
> че-нить поменяют?

Я и не предлагаю использовать GetMem для динамических массивов. Я говорил про использование GetMem для типов-указателей на статический массив. И имел в виду, что выделение одного и того же объёма памяти для соответствующих типов этими двумя способами сильно различается по скорости.

> alertus   (25.05.05 18:47) [49]

> Кстати, статитические массивы примерно в 3 раза быстрее
> работают, сегодня проверял.

Повторю: если речь идёт о скорости доступа к элементу, то такого не может быть.


 
alertus   (2005-05-25 19:00) [51]


> Повторю: если речь идёт о скорости доступа к элементу, то
> такого не может быть.

А какое может быть??

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


 
begin...end ©   (2005-05-25 19:02) [52]

> alertus   (25.05.05 19:00) [51]

Код проверки можно увидеть?


 
alertus   (2005-05-25 19:02) [53]

Может я чего-то не понимаю, но такой код:
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 каждый раз.


 
alertus   (2005-05-25 19:08) [54]


> Код проверки можно увидеть?

Для динамического:
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;


 
begin...end ©   (2005-05-25 19:13) [55]

> 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:16) [56]

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


 
Mx ©   (2005-05-25 19:17) [57]


> alertus   (25.05.05 19:02) [53]

Насчет примера: Initialize вызывать не надо. Если ты сравниваешь скорость всей ButtonXClick, то возможно ее съедает SetLength, но доступ по скорости не может отличаться.


> Кстати, если я правильно понял, то и в окне CPU видно, что
> программа обращается к memo1.Lines.Count каждый раз

Естественно! Каждый раз в цикле по I, но не по J.


 
alertus   (2005-05-25 19:22) [58]

Я сравниваю скорость по этому фрагменту:
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 раза.


 
begin...end ©   (2005-05-25 19:22) [59]

> alertus   (25.05.05 19:08) [54]

Выполнил оба кода. В 3 раза времена выполнения не различаются. Они почти одинаковые. Что я делаю не так?

P.S. В данном случае не надо ни Initialize, ни Finalize.


 
alertus   (2005-05-25 19:24) [60]

Не знаю, я на двух машинах тестировал, на работе -- в 3 раза, дома -- в 2.
Может от машины зависит??


 
alertus   (2005-05-25 19:26) [61]

Кстати, надо выставить цикл побольше, у меня порядка
i:=1 to 1000000000 а в маленьком цикле разницы конечно нет, если счет идет на 1-2 секунды...


 
Mx ©   (2005-05-25 19:28) [62]


> begin...end ©   (25.05.05 19:22) [59]
> В данном случае не надо ни Initialize, ни Finalize.

Верно говоришь.


 
Mx ©   (2005-05-25 19:30) [63]

Интересно, а почему в CPU View коды выполнения умножений различаются? С дин.массивами на пару инструкций больше.


 
alertus ©   (2005-05-25 19:33) [64]


> Интересно, а почему в CPU View коды выполнения умножений
> различаются? С дин.массивами на пару инструкций больше.

Факт!


 
begin...end ©   (2005-05-25 19:33) [65]

> Mx ©   (25.05.05 19:30) [63]

Потому что в случае с дин. массивами требуется двойное разыменование.


 
Kolan ©   (2005-05-25 19:34) [66]

Я что-то наверно упустил. Но вот как я вижу сеть:
Есть класс Нейронн(веса итд).
И есть класс Сеть - массив нейронов. Да?
Потом как ты меняешь веса итд может у тебя алгоритм обратного распространения ошибки неоптимальный? А в maple можно исходник посмотреть? Передири тот алгоритм что там.


 
alertus ©   (2005-05-25 19:37) [67]

Я весь алгоритм передрал из Matcad, включая алгоритм Левенберга-Марквардта, самый быстрый алгоритм обучения полносвязных сетей.
А насчет видения сетей, есть как минимум две идеалогии:
1) сеть - массив слоев, слой - массив нейронов, нейрон - массив параметров;
2) сеть - массив слоев, слой - набор матриц параметров (веса, смещения, входы, выходы, всякие вспомогательные).


 
Kolan ©   (2005-05-25 19:44) [68]

Ну вот я делал 1. Но я схитрил и взял 1 слой. И сё.
Так а ты как сделал?
Я думая тут дело в алгоритме или еще где.


 
alertus ©   (2005-05-25 19:47) [69]

1 слой - плохо, 3 и больше тоже, два - хорошо.
А еще алгоритмы обучения никуда не годятся, только Левенберг-Марквардт.
Моя программа может аппроксимировать функции, находит коэффициенты рядов Тэйлора и Фурье, распознает образы и многое многое другое, но ПРОБЛЕМА!!! циклы обучения медленные, виноват не алгоритм, а реализация, в Matlab"бе же все работает точно также, но в 5 раз быстрее.


 
Kolan ©   (2005-05-25 19:51) [70]


> alertus ©   (25.05.05 19:47) [69]
> 1 слой - плохо, 3 и больше тоже, два - хорошо.
> А еще алгоритмы обучения никуда не годятся, только Левенберг-Марквардт.
> Моя программа может аппроксимировать функции, находит коэффициенты
> рядов Тэйлора и Фурье, распознает образы и многое многое
> другое, но ПРОБЛЕМА!!! циклы обучения медленные, виноват
> не алгоритм, а реализация, в Matlab"бе же все работает точно
> также, но в 5 раз быстрее.


Wowwwwww :)

TMatrix = class(TObject)
private
  w:integer;  //ширина матрицы
  h:integer;  //высота матрицы
  elem:array of array of real;  //массив элементов матрицы
public
  constructor Create(wset,hset:integer); virtual;
  destructor Destroy; override;
  function GetElem(i,j:integer):real;
  procedure SetElem(i,j:integer;r:real);
  function GetWidth:integer;
  function GetHeight:integer;
end;


Мож вот в этом проблема. Зачем делать матрицу наследником TObject?


 
alertus ©   (2005-05-25 19:56) [71]

Не знаю, я посмотрел модуль NeuralBase, их пишет BaseGroup (там возможностей маловато, но написан он вроде профессионально), я объявления классов у них содрал...
Даже классы исключений написал, хотя они мне не нужны, так, для красоты.
Просто я в объявления классов не силен...
А как лучше сделать??


 
alertus ©   (2005-05-25 20:12) [72]

Может кто-нибудь поправит, вот часть кода:

unit MatrixCalc;

interface

uses SysUtils,Classes;

const
 SWrongColIndex = "Индекс столбца %d не соответствует размерности матрицы.";
 SWrongRowIndex = "Индекс строки %d не соответствует размерности матрицы.";
 SWrongSizesAdd = "Размеры матриц при сложении не соответсвуют."+chr(13)+"Ширина и высота первой матрицы должны совпадать с шириной и высотой второй матрицы.";
 SWrongSizesSubtract = "Размеры матриц при вычитании не соответсвуют."+chr(13)+"Ширина и высота первой матрицы должны совпадать с шириной и высотой второй матрицы.";
 SWrongSizesMult = "Размеры матриц при умножении не соответствуют."+chr(13)+"Ширина первой матрицы должна быть равна высоте второй.";
 SWrongSizesLeftDiv = "Размеры матриц при левом делении не соответствуют."+chr(13)+"Высота первой матрицы должна быть равна высоте второй матрицы.";
 SNotSqSizeLeftDiv = "При левом делении первая матрица должна быть квадратной,"+chr(13)+"Иначе получается недоопределенная или переопределенная система."+chr(13)+"Такие системы тоже можно решать, но это будет поддерживаться в следующей версии.";
 SWrongStringReadMatrix = "Неверный формат строки при конвертации строки в матрицу.";
 SWrongHeightsGlue = "Высоты матриц при горизонтальном склеивании не должны различаться.";
 SWrongWidthsGlue = "Ширины матриц при вертикальном склеивании не должны различаться.";
 SWrongIndexesFrag = "При получении фрагмента матрицы заданы неверные индексы:"+chr(13)+"левая верхняя ячейка (%d,%d), правая нижняя ячейка (%d,%d).";

type
 EMatrixError = class(Exception);

 TMatrix = class(TObject)
 private
   w:integer;  //ширина матрицы
   h:integer;  //высота матрицы
   elem:array of array of real;  //массив элементов матрицы
 public
   constructor Create(wset,hset:integer); virtual;
   destructor Destroy; override;
   function GetElem(i,j:integer):real;
   procedure SetElem(i,j:integer;r:real);
   function GetWidth:integer;
   function GetHeight:integer;
 end;

function WriteMatrix(m:TMatrix;escape:boolean):string; //выводит матрицу в строку
function ReadMatrix(s:string;escape:boolean):TMatrix; //читает матрицу из строки
function EyeMatrix(wset,hset:integer;mu:real):TMatrix;  //генерирует матрицу у которой на главной диагонали стоят mu, остальные - нули
function FillMatrix(wset,hset:integer;r:real):TMatrix;  //Заполняет матрицу числом r
function EquateMatrix(m:TMatrix):TMatrix;  //приравнивает матрицы, т.е. создает клон матрицы
function AddNumber(m:TMatrix;r:real):TMatrix;  //прибавляет к каждому элементу матрицы число
function AddMatrix(m1,m2:TMatrix):TMatrix;  //поэлементное сложение матриц
function SubtractMatrix(m1,m2:TMatrix):TMatrix;  //поэлементное вычитание матриц m1-m2
function MultMatrix(m1,m2:TMatrix):TMatrix;  //умножение матриц
function TransMatrix(m:TMatrix):TMatrix;  //транспонирование матрицы
function LeftDivMatrix(m1,m2:TMatrix):TMatrix;  //левое деление матриц, или решение системы линейных уравнений, где первая матрица - коэфициенты левой части, а столбцы второй - правая часть системы, столбцы результата будут решением системы
function GlueMatrix(m1,m2:TMatrix;horiz:boolean):TMatrix;  //Склеивает две матрицы, horiz=true - по горизонтали, horiz=false - по вертикали
function ColMatrix(m:TMatrix;i:integer):TMatrix;  //выводит колонку с номером  n матрицы m в вектор-столбец (матрица-результат)
function FragMatrix(m:TMatrix;i1,j1,i2,j2:integer):TMatrix;  //возвращает матрицу-фрагмент входной матрицы
function SqSumMatrix(m:TMatrix):real;  //возвращает сумму квадратов всех элементов матрицы

implementation

constructor TMatrix.Create(wset,hset:integer);
var
 i,j:integer;
begin
 inherited Create;
 Initialize(elem);
 SetLength(elem,wset,hset);
 w:=wset;
 h:=hset;
 for i:=1 to wset do begin
   for j:=1 to hset do begin
     SetElem(i,j,0);
   end;
 end;
end;

destructor TMatrix.Destroy;
begin
 Finalize(elem);
 inherited Destroy;
end;

function TMatrix.GetElem(i,j:integer):real;
begin
 if (i<1) or (i>w) then begin
   raise EMatrixError.CreateFmt(SWrongColIndex,[i]);
   exit;
 end;
 if (j<1) or (j>h) then begin
   raise EMatrixError.CreateFmt(SWrongRowIndex,[j]);
   exit;
 end;
 result:=elem[i-1,j-1];
end;

procedure TMatrix.SetElem(i,j:integer;r:real);
begin
 if (i<1) or (i>w) then begin
   raise EMatrixError.CreateFmt(SWrongColIndex,[i]);
   exit;
 end;
 if (j<1) or (j>h) then begin
   raise EMatrixError.CreateFmt(SWrongRowIndex,[j]);
   exit;
 end;
 elem[i-1,j-1]:=r;
end;

function TMatrix.GetWidth:integer;
begin
 result:=w;
end;

function TMatrix.GetHeight:integer;
begin
 result:=h;
end;


 
Mx ©   (2005-05-25 20:15) [73]


> begin...end ©   (25.05.05 19:33) [65]
> Потому что в случае с дин. массивами требуется двойное
> разыменование.

Что такое "разыменование"? Указатель на указатель?


 
begin...end ©   (2005-05-25 20:29) [74]

> Mx ©   (25.05.05 20:15) [73]

> Что такое "разыменование"?

Операция получения содержимого памяти по значению указателя.

Что нужно сделать, чтобы получить значение i-го элемента статического массива (будем считать, что он индексируется с нуля), являющегося локальной переменной? Нужно к адресу этой переменной (в стеке) прибавить i * размер_элемента_массива байт, а затем разыменовать полученный указатель.

А как обстоит дело с локальной переменной динамического массива? Переменная является указателем на область памяти, в которой находятся элементы. Значит, вначале нужно разыменовать адрес переменной в стеке, чтобы получить этот указатель. А потом уже к тому, что получилось, прибавить i * размер_элемента_массива байт. И после этого -- разыменовать, для получения значения элемента.


 
alertus ©   (2005-05-25 20:29) [75]

Разыменование указателя - получение значения ячейки памяти, куда он ссылается

a:integer;
b:integer;
p:^integer;
...
a:=5;
p:=@a;
b:=p^;


Последняя операция - разыменование указателя.
Итого: b=5.


 
alertus ©   (2005-05-25 20:31) [76]

2begin...end
Но ведь все эти операции требуют времени, значит работа с динамическими массивами медленнее.


 
begin...end ©   (2005-05-25 20:32) [77]

> alertus ©   (25.05.05 20:31) [76]

Немного медленнее, но не в 3 раза.


 
Mx ©   (2005-05-25 20:34) [78]


> alertus ©   (25.05.05 20:31) [76]
> begin...end ©

Пасиб!



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

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

Наверх




Память: 0.69 MB
Время: 0.038 c
3-1115349628
SergP
2005-05-06 07:20
2005.06.14
Как работать с таблицами DBASE IV имеющими расширение не dbf?


1-1117506788
AntohaKET
2005-05-31 06:33
2005.06.14
Как связать две форма с переменными на первой.


1-1117278587
syama
2005-05-28 15:09
2005.06.14
как зделать align manually?


3-1115801764
Lex_!
2005-05-11 12:56
2005.06.14
DBGrid + hint


14-1116326649
Андрей Жук
2005-05-17 14:44
2005.06.14
Free Pascal 2.0





Afrikaans Albanian Arabic Armenian Azerbaijani Basque Belarusian Bulgarian Catalan Chinese (Simplified) Chinese (Traditional) Croatian Czech Danish Dutch English Estonian Filipino Finnish French
Galician Georgian German Greek Haitian Creole Hebrew Hindi Hungarian Icelandic Indonesian Irish Italian Japanese Korean Latvian Lithuanian Macedonian Malay Maltese Norwegian
Persian Polish Portuguese Romanian Russian Serbian Slovak Slovenian Spanish Swahili Swedish Thai Turkish Ukrainian Urdu Vietnamese Welsh Yiddish Bengali Bosnian
Cebuano Esperanto Gujarati Hausa Hmong Igbo Javanese Kannada Khmer Lao Latin Maori Marathi Mongolian Nepali Punjabi Somali Tamil Telugu Yoruba
Zulu
Английский Французский Немецкий Итальянский Португальский Русский Испанский