-
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
-
Лучше приводить код полностью и показывать где у вас «тормоза». Если функция вызывается поэлементно, то в декларации пометьте ее как inline; Если складываете в третий массив, то лучше делать в процедуре и передавать в нее 3 указателя. .NET'а тут у вас не видно. Это просто работа с памятью.
-
Почитал хэлп к сожалению не получится inline; type TBigData = array of UInt64; procedure Add_pr(Var arr_to: TBigData; Const arr_from: TBigData);
* Подпрограммы, принимающие в качестве параметров открытые массивы, не могут быть встроены.
-
Можно передавать указатели на начало массива.
-
Есть еще какие-то дерективы для ускорения, кроме того, параметры компилирования, не загружать то и это, не проверять границы или еще какие-то хитрости?
Есть ли вообще интересная литература об этом, статьи?
-
Можно так:
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;
-
Ассемблер плохо знаю, но у меня не пойдет, 64 битный. Там rax/r10 UInt64 - беззнаковый 64 как под него код переписать?
-
Я так понимаю еще мне нужно учитывать CL или как то так, который говорит об переполнении, когда при сложении вышли из рамок самой максимума цифры.
Типа такого беззнаковый byte: 200+200=400 mod 256= 144;
-
> AntVa (17.12.18 20:26) > > Delphi 10.2 на .Net > > Насколько знаю при передачи массива в процедуру/функцию, > передаётся её адрес, но не сам массив.
Вообще-то это знание относится к временам царя Гороха, когда массивы были только статическими.
-
>Ассемблер плохо знаю, но у меня не пойдет, 64 битный. >Там rax/r10
У меня универсальный код: 32/64 бита. Где вы там rax/r10 увидели? Там EAX.
>UInt64 - беззнаковый 64 >как под него код переписать? Count объявите не как integer, а как dword
-
>UInt64 - беззнаковый 64 >как под него код переписать?
А здесь вам 64 бита уже нужен? Вы определитесь, что вам нужно.
-
type TBigData = array of UInt64; Это в самом начале первого еще вопроса было.
Всегда нужен был только 64 битный беззнаковый, то есть на максимум загружаю, тем самым массив становится меньше, в смысле количество его членов, при одном и том же массиве. Да и чисто интуитивно, например сложение, один раз сложить 64 бита быстрее, чем если складывать на два 32 битных числа и сложить два раза. По сути складываем одно и тоже, но проверка в 32б на выполнения дважды, сложение тоже дважды. А при 64 единожды складываем, единожды проверка на переполнение.
-
@У меня универсальный код: 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"
-
Пытался топорно переделать(множество изменений пытался провести) из не работающего кода 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;
-
Создайте пустое консольное приложение. Добавьте платформу 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.
-
Немного скорректировал. Последний вариант более точный.
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.
-
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); то всё не работает, та же ошибка, массив не существует, с чем это связано, можно ли обойти как-нить?
-
Модификация для цикла:
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.
-
Да работает! Спс большое!
А как в асм увеличить размер массива и добавить к след.элементу массива единицу при переполнении в сложении? Это нужно для полноценного сложения:
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;
Вроде только после этого можно сказать что сложение длинных чисел работает.
-
Перенос - флаг 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-команды.
|