Мастера DELPHI, Delphi programming community Рейтинг@Mail.ru Титульная страница Поиск, карта сайта Написать письмо 
| Новости |
Новости сайта
Поиск |
Поиск по лучшим сайтам о Delphi
FAQ |
Огромная база часто задаваемых вопросов и, конечно же, ответы к ним ;)
Статьи |
Подборка статей на самые разные темы. Все о DELPHI
Книги |
Новинки книжного рынка
Новости VCL
Обзор свежих компонент со всего мира, по-русски!
|
| Форумы
Здесь вы можете задать свой вопрос и наверняка получите ответ
| ЧАТ |
Место для общения :)
Орешник
Коллекция курьезных вопросов из форумов
Основная («Начинающим»)/ Базы / WinAPI / Компоненты / Сети / Media / Игры / Corba и COM / KOL / FreePascal / .Net / Прочее / rsdn.org

 
Чтобы не потерять эту дискуссию, сделайте закладку « предыдущая ветвь | форум | следующая ветвь »

Работа с внешним массивом внутри функции переданным как параметр


AntVa   (17.12.18 20:26

Delphi 10.2 на .Net

Насколько знаю при передачи массива в процедуру/функцию, передаётся её адрес, но не сам массив.

type
 TBigData = array of UInt64;

procedure Add_pr(Var arr_to: TBigData; Const arr_from: TBigData); --суммирует два массива, результат пишет в первый
begin
  arr_to[i] := arr_to[i] + arr_from[i];//Код пропущен, просто суммирование двух массивов в первый
end;

function Add_fu(Const arr_to, arr_from: TBigData): TBigData; --суммирует два массива, результат пишет в третий (fun.Result)
begin
  Result[i] := arr_to[i] + arr_from[i];//Код пропущен, просто суммирование двух массивов в третий
end;


Иногда нужно суммировать в один из двух массивов, которые слагаемые, иногда нужно вообще новый создать(третий) и туда занести сумму.

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

Как объединить код проц/функ не потеряв скорости алгоритма?
Что-то вроде

function Add_fu(Var arr_to: TBigData; Const arr_from: TBigData): TBigData;
begin
  Result[i] := arr_to[i] + arr_from[i];
end;

При вызове если нужно работать создать третий массив:
arr3 := Add_fu(arr1,arr2);
Если нужно занести сумму в первый массив слагаемое, третий массив-результат не нужен:
arr1 := Add_fu(arr1,arr2);

Можно ли обойтись малой кровью не потеряв скорости работы алгоритма, с минимум изменений.
Не будет ли при таком вызове (arr1 := Add_fu(arr1,arr2)) проблем и создания копий и прочих тормозов и ошибок?
Вроде бы внутри функции Result, arr_to это просто ссылка на адрес, и в зависимости от вызова функции они могут быть одним тем же или разным массивами, но кто знает, как это реализовано в Delphi .Net


dmk ©   (17.12.18 21:33[1]

Лучше приводить код полностью и показывать где у вас «тормоза».
Если функция вызывается поэлементно, то в декларации пометьте ее как inline;
Если складываете в третий массив, то лучше делать в процедуре и передавать в нее 3 указателя.
.NET'а тут у вас не видно. Это просто работа с памятью.


AntVa   (17.12.18 22:07[2]

Почитал хэлп к сожалению не получится inline;
type
TBigData = array of UInt64;
procedure Add_pr(Var arr_to: TBigData; Const arr_from: TBigData);

* Подпрограммы, принимающие в качестве параметров открытые массивы, не могут быть встроены.


dmk ©   (17.12.18 22:23[3]

Можно передавать указатели на начало массива.


AntVa   (17.12.18 22:51[4]

Есть еще какие-то дерективы для ускорения, кроме того, параметры компилирования, не загружать то и это, не проверять границы или еще какие-то хитрости?

Есть ли вообще интересная литература об этом, статьи?


dmk ©   (17.12.18 23:03[5]

Можно так:

const
 A: array[0..1] of Integer = (1, 2);
 B: array[0..1] of Integer = (3, 4);

procedure AddInteger(A, B: Pointer); inline;
begin
 PInteger(A)^ := PInteger(A)^ + PInteger(B)^;
end;

procedure AddIntegerToA(A, B: Pointer; Count: Integer);
asm
@N:
 mov eax, dword ptr [A]
 add eax, dword ptr [B]
 mov dword ptr [A], eax

 add A, 4 //Смещение к следующей ячейке массива
 add B, 4 //Смещение к следующей ячейке массива

 dec Count
 jnz @N
end;

procedure Main;
begin
 AddInteger(@A[0], @B[0]); //<- Вариант сложения 1 (поэлементно)
 AddIntegerToA(@A, @B, Length(A)); //<- Вариант сложения 2 (весь массив)
end;


AntVa   (18.12.18 00:02[6]

Ассемблер плохо знаю, но у меня не пойдет, 64 битный.
Там rax/r10
UInt64 - беззнаковый 64
как под него код переписать?


AntVa   (18.12.18 00:05[7]

Я так понимаю еще мне нужно учитывать CL или как то так, который говорит об переполнении, когда при сложении вышли из рамок самой максимума цифры.

Типа такого беззнаковый byte: 200+200=400 mod 256= 144;


Германн ©   (18.12.18 02:03[8]


> AntVa   (17.12.18 20:26)
>
> Delphi 10.2 на .Net
>
> Насколько знаю при передачи массива в процедуру/функцию,
>  передаётся её адрес, но не сам массив.

Вообще-то это знание относится к временам царя Гороха, когда массивы были только статическими.


dmk ©   (18.12.18 02:41[9]

>Ассемблер плохо знаю, но у меня не пойдет, 64 битный.
>Там rax/r10

У меня универсальный код: 32/64 бита.
Где вы там rax/r10 увидели? Там EAX.

>UInt64 - беззнаковый 64
>как под него код переписать?
Count объявите не как integer, а как dword


dmk ©   (18.12.18 02:43[10]

>UInt64 - беззнаковый 64
>как под него код переписать?

А здесь вам 64 бита уже нужен? Вы определитесь, что вам нужно.


AntVa   (18.12.18 12:46[11]

type TBigData = array of UInt64; Это в самом начале первого еще вопроса было.

Всегда нужен был только 64 битный беззнаковый, то есть на максимум загружаю, тем самым массив становится меньше, в смысле количество его членов, при одном и том же массиве. Да и чисто интуитивно, например сложение, один раз сложить 64 бита быстрее, чем если  складывать на два 32 битных числа и сложить два раза. По сути складываем одно и тоже, но проверка в 32б на выполнения дважды, сложение тоже дважды. А при 64 единожды складываем, единожды проверка на переполнение.


AntVa   (18.12.18 12:56[12]

@У меня универсальный код: 32/64 бита.
Где вы там rax/r10 увидели? Там EAX.@
Поэтому и спросил как корректно перевести на rax/r10, т.к. с 64 битами код не работает!
Вот пример, объявили два массива, в каждом по 4 члена. При попытке обратиться к массиву после сложения, вылетает ошибка, что массив не объявлен?
"c00000005 Access_violation"

Глянул в Watches:
massiv = ()
massiv2 = (18446744073709551614, 0, 6553, 5)

type TBigData = array of UInt64;

****

 Setlength(massiv,4);
 massiv[0] := $fffffffffffffffe;
 massiv[1] := $0;
 massiv[2] := $999;
 massiv[3] := $2;
 Setlength(massiv2,4);
 massiv2[0] := $fffffffffffffffe;
 massiv2[1] := $0;
 massiv2[2] := $1999;
 massiv2[3] := $5;

 AddIntegerToA(@massiv, @massiv2, Length(massiv));

ShowMessage( inttostr(length(massiv)));//"c00000005 Access_violation"


AntVa   (18.12.18 12:58[13]

Пытался топорно переделать(множество изменений пытался провести) из не работающего кода 32бита в 64бита тоже не работает :(
та же самая ошибка.

procedure AddIntegerToA(A, B: Pointer; Count: dword);
asm
@N:
 mov eax, dword ptr [A]
 add eax, dword ptr [B]
 mov dword ptr [A], eax

 add A, 4 //Смещение к следующей ячейке массива
 add B, 4 //Смещение к следующей ячейке массива

 dec Count
 jnz @N
end;

asm
@N:
 mov rax, UInt64 ptr [A]
 add rax, UInt64 ptr [B]
 mov UInt64 ptr [A], rax

 add A, 8 //Смещение к следующей ячейке массива
 add B, 8 //Смещение к следующей ячейке массива

 dec Count
 jnz @N
end;


dmk ©   (18.12.18 16:17[14]

Создайте пустое консольное приложение.
Добавьте платформу 64 бита.
Полностью замените шаблонный текст на мой:
program ArrayAB;

{$APPTYPE CONSOLE}

{$R *.res}

uses
 System.SysUtils;

//------------------------------------------------------------------------------

type QWord = UInt64;

//------------------------------------------------------------------------------

var
 qA: array[0..1] of QWord = (7, 2);
 qB: array[0..1] of QWord = (3, 8);
 qC: array[0..1] of QWord = (0, 0);

 iA: array[0..1] of Integer = (1, 2);
 iB: array[0..1] of Integer = (3, 4);
 iC: array[0..1] of Integer = (0, 0);

//------------------------------------------------------------------------------

procedure AddInteger32(A, B, C: Pointer); inline;
begin
 PInteger(C)^ := PInteger(A)^ + PInteger(B)^;
end;

//------------------------------------------------------------------------------

procedure AddIntegerToC32(A, B, Result: Pointer; Count: Integer);
asm
@N:
 mov eax, dword ptr [A]
 add eax, dword ptr [B]
 mov dword ptr [Result], eax

 //Смещение к следующей ячейке массива
 add A, 4
 add B, 4

 dec Count
 jnz @N
end;

//------------------------------------------------------------------------------

procedure AddIntegerToC64(A, B, C: Pointer; Count: QWord);
asm
@N:
 mov rax, qword ptr [A]
 add rax, qword ptr [B]
 mov qword ptr [C], rax

 //Смещение к следующей ячейке массива
 add A, 8
 add B, 8
 add C, 8

 dec Count
 jnz @N
end;

//------------------------------------------------------------------------------

begin
 Writeln('Складываем iA + iB');
 Writeln('iA: ' + IntToStr(iA[0]) + ' ' + IntToStr(iA[1]));
 Writeln('iB: ' + IntToStr(iB[0]) + ' ' + IntToStr(iB[1]));
 Writeln;

 //Вариант сложения 1 (поэлементно)
 AddInteger32(@iA[0], @iB[0], @iC[0]);
 AddInteger32(@iA[1], @iB[1], @iC[1]);

 Writeln('Получается:');
 Writeln('iC: ' + IntToStr(iC[0]) + ' ' + IntToStr(iC[1]));
 Readln;

 Writeln('------------------');
 Writeln;

 Writeln('Складываем qA + qB');
 Writeln('qA: ' + IntToStr(qA[0]) + ' ' + IntToStr(qA[1]));
 Writeln('qB: ' + IntToStr(qB[0]) + ' ' + IntToStr(qB[1]));
 Writeln;

 //Вариант сложения 2 (весь массив)
 AddIntegerToC64(@qA, @qB, @qC, Length(qA));

 Writeln('Получается:');
 Writeln('iC: ' + IntToStr(qC[0]) + ' ' + IntToStr(qC[1]));
 Readln;
end.


dmk ©   (18.12.18 17:38[15]

Немного скорректировал.
Последний вариант более точный.
program ArrayAB;

{$APPTYPE CONSOLE}

{$R *.res}

uses
 System.SysUtils;

//------------------------------------------------------------------------------

type QWord = UInt64;

//------------------------------------------------------------------------------

var
 qA: array[0..2] of QWord = (7, 2, 1);
 qB: array[0..2] of QWord = (3, 8, 4);
 qC: array[0..2] of QWord = (0, 0, 0);

 iA: array[0..2] of Integer = (1, 2, 3);
 iB: array[0..2] of Integer = (3, 4, 9);
 iC: array[0..2] of Integer = (0, 0, 0);

//------------------------------------------------------------------------------

procedure AddInt32(A, B, C: Pointer); inline;
begin
 PInteger(C)^ := PInteger(A)^ + PInteger(B)^;
end;

//------------------------------------------------------------------------------

procedure AddIntegerToC32(A, B, C: Pointer; Count: Integer);
asm
@N:
 mov eax, dword ptr [A]
 add eax, dword ptr [B]
 mov dword ptr [C], eax

 //Смещение к следующей ячейке массива
 add A, 4
 add B, 4
 add C, 4

 dec Count
 jnz @N
end;

//------------------------------------------------------------------------------

procedure AddQWordArray64(A, B, C: Pointer; Count: QWord);
asm
@N:
 mov rax, qword ptr [A]
 add rax, qword ptr [B]
 mov qword ptr [C], rax

 //Смещение к следующей ячейке массива
 add A, 8
 add B, 8
 add C, 8

 dec Count
 jnz @N
end;

//------------------------------------------------------------------------------

begin
 Writeln('Складываем iA + iB');
 Writeln('iA: ' + IntToStr(iA[0]) + ' ' + IntToStr(iA[1]) + ' ' + IntToStr(iA[2]));
 Writeln('iB: ' + IntToStr(iB[0]) + ' ' + IntToStr(iB[1]) + ' ' + IntToStr(iB[2]));
 Writeln;

 //Вариант сложения 1 (поэлементно)
 AddInt32(@iA[0], @iB[0], @iC[0]);
 AddInt32(@iA[1], @iB[1], @iC[1]);
 AddInt32(@iA[2], @iB[2], @iC[2]);

 Writeln('Получается:');
 Writeln('iC: ' + IntToStr(iC[0]) + ' ' + IntToStr(iC[1]) + ' ' + IntToStr(iC[2]));
 Readln;

 Writeln('------------------');
 Writeln;

 Writeln('Складываем qA + qB');
 Writeln('qA: ' + IntToStr(qA[0]) + ' ' + IntToStr(qA[1]) + ' ' + IntToStr(qA[2]));
 Writeln('qB: ' + IntToStr(qB[0]) + ' ' + IntToStr(qB[1]) + ' ' + IntToStr(qB[2]));
 Writeln;

 //Вариант сложения 2 (весь массив)
 AddQWordArray64(@qA, @qB, @qC, Length(qA));

 Writeln('Получается:');
 Writeln('iC: ' + IntToStr(qC[0]) + ' ' + IntToStr(qC[1]) + ' ' + IntToStr(qC[2]));
 Readln;
end.


AntVa   (18.12.18 20:41[16]

procedure TForm1.Button5Click(Sender: TObject);
var
 qA: array[0..2] of QWord;
 qB: array[0..2] of QWord;
 qC: array[0..2] of QWord;
begin
 qA[0] := $fffffffffffffffe;
 qA[1] := $0;
 qA[2] := $999;
 qB[0] := $fffffffffffffffe;
 qB[1] := $0;
 qB[2] := $1999;
AddQWordArray64(@qA, @qB, @qC, Length(qA));
 for i := 0 to length(qC)-1 do
   ShowMessage( Format('%x', [qC[i]]));

Да все верно так работает, если массив объявлен размером до компиляции.

Но если массив динамический!
 qC: array of QWord;
***
 Setlength(qC,3);
то всё не работает, та же ошибка, массив не существует, с чем это связано, можно ли обойти как-нить?


dmk ©   (18.12.18 21:53[17]

Модификация для цикла:
program ArrayAB;

{$APPTYPE CONSOLE}

{$R *.res}

uses
 System.SysUtils;

//------------------------------------------------------------------------------

type QWord = UInt64;

//------------------------------------------------------------------------------

var
 qA: array of QWord;
 qB: array of QWord;
 qC: array of QWord;

 iA: array of Integer;
 iB: array of Integer;
 iC: array of Integer;

 N, i: Integer;

//------------------------------------------------------------------------------

procedure AddInt32(A, B, C: Pointer); inline;
begin
 PInteger(C)^ := PInteger(A)^ + PInteger(B)^;
end;

//------------------------------------------------------------------------------

procedure AddIntegerToC32(A, B, C: Pointer; Count: Integer);
asm
@N:
 mov eax, dword ptr [A]
 add eax, dword ptr [B]
 mov dword ptr [C], eax

 //Смещение к следующей ячейке массива
 add A, 4
 add B, 4
 add C, 4

 dec Count
 jnz @N
end;

//------------------------------------------------------------------------------

procedure AddQWordArray64(A, B, C: Pointer; Count: QWord);
asm
 //Извлекаем из переменных
 //адреса массивов
 mov A, [A]
 mov B, [B]
 mov C, [C]

@N:
 mov rax, qword ptr [A]
 add rax, qword ptr [B]
 mov qword ptr [C], rax

 //Смещение к следующей ячейке массива
 add A, 8
 add B, 8
 add C, 8

 dec Count
 jnz @N
end;

//------------------------------------------------------------------------------

begin
 N := 10;

 SetLength(iA, N);
 SetLength(iB, N);
 SetLength(iC, N);

 SetLength(qA, N);
 SetLength(qB, N);
 SetLength(qC, N);

 Randomize;

 for i := Low(iA) to High(iA) do iA[i] := Random($FF);
 for i := Low(iB) to High(iB) do iB[i] := Random($FF);
 for i := Low(iC) to High(iC) do iC[i] := Random($FF);

 for i := Low(qA) to High(qA) do qA[i] := Random($FFFF);
 for i := Low(qB) to High(qB) do qB[i] := Random($FFFF);
 for i := Low(qC) to High(qC) do qC[i] := Random($FFFF);

 Writeln('Складываем iA + iB');
 Writeln;

 //Вариант сложения 1 (поэлементно)
 for i := Low(iA) to High(iA) do
 begin
   AddInt32(@iA[i], @iB[i], @iC[i]);
   Writeln(IntToStr(i) + ': ' + IntToStr(iC[i]) + ' = ' + IntToStr(iA[i]) + ' + ' + IntToStr(iB[i]));
 end;

 Readln;

 Writeln('------------------');
 Writeln;

 Writeln('Складываем qA + qB');
 Writeln;

 //Вариант сложения 2 (весь массив)
 AddQWordArray64(@qA, @qB, @qC, N);

 for i := Low(qA) to High(qA) do
 begin
   Writeln(UIntToStr(i) + ': ' + UIntToStr(qC[i]) + ' = ' + UIntToStr(qA[i]) + ' + ' + UIntToStr(qB[i]));
 end;

 N := 0;

 SetLength(iA, N);
 SetLength(iB, N);
 SetLength(iC, N);

 SetLength(qA, N);
 SetLength(qB, N);
 SetLength(qC, N);

 Readln;
end.


AntVa   (18.12.18 22:56[18]

Да работает! Спс большое!

А как в асм увеличить размер массива и добавить к след.элементу массива единицу при переполнении в сложении?
Это нужно для полноценного сложения:

 mov rax, qword ptr [A]
 add rax, qword ptr [B]
 mov qword ptr [C], rax

 //Смещение к следующей ячейке массива
 add A, 8
 add B, 8
 add C, 8
 adc C //если при сложении qa[i]+qb[i] переполнились и вышли из диапазона.

Например:
qa[3]=$8000 0000 0000 0001;
qb[3]=$8000 0000 0000 0001;
Сумма будет: qc[3]=$2;
и признак переполнения CF, вроде можно вырулить командой adc, чтобы прибавить 1 к следующему элементу массива qc[4]=qc[4]+1 образно выражаясь кодом конечно, он не верен.
Как еще учесть, что при добавлении +1 тоже может быть переполнение массива и так по кругу.

qA[0] := $ffffffffffffffff;
qA[1] := $ffffffffffffffff;
qA[2] := $ffffffffffffffff;
qB[0] := $ffffffffffffffff;
qB[1] := $ffffffffffffffff;
qB[2] := $ffffffffffffffff;

то нынешний код даст нам
qс[0] := $fffffffffffffffe;
qс[1] := $fffffffffffffffe;
qс[2] := $fffffffffffffffe;

Если попытаться прибавить через adc еще при сложении в 0 элементе и переполнении, то прибавлять некуда  qA[1] и qB[1] тут некуда прибавить 1 лишнюю, они и так переполнены.

настоящий ответ:
qс[0] := $fffffffffffffffe;
qс[1] := $ffffffffffffffff;
qс[2] := $ffffffffffffffff;
qс[2] := $1;
Попробуйте в коде набить эти цифры и посмотреть ответ:
 for i := 0 to length(qс)-1 do
   ShowMessage( Format('%x', [qс[i]]));

2)Кроме того нужно учитывать, что все может быть 3 элемента массива в каждом массиве, и при переполнении сложения занести лишнюю единицу будет некуда, т.к. qс[4] не существует, значит нужно выделить setlength(qс,length(qс)+1) и только потом qс[4]:=1;

Вроде только после этого можно сказать что сложение длинных чисел работает.


dmk ©   (18.12.18 23:27[19]

Перенос - флаг CF

 // Перед циклом xor r10, r10
 setc r10b
 add rax, r10

Только позаботится о переносе вы сами должны. Это для типа Integer.
Если вам 128 битное число нужно, то массив должен быть A: array of DQWord;
type DQWord = array[0..1] of QWord; //128 бит
type QQWord = array[0..3] of QWord; //256 бит
Отлавливать флаги и учитывать заполнение старших разрядов придется вручную
или использовать AVX-команды.


версия для печати

Написать ответ

Ваше имя (регистрация  E-mail 







Разрешается использование тегов форматирования текста:
<b>жирный</b> <i>наклонный</i> <u>подчеркнутый</u>,
а для выделения текста программ, используйте <code> ... </code>
и не забывайте закрывать теги! </b></i></u></code> :)


Наверх

  Рейтинг@Mail.ru     Титульная страница Поиск, карта сайта Написать письмо