Конференция "Начинающим" » Работа с внешним массивом внутри функции переданным как параметр
 
  • AntVa (17.12.18 20:26) [0]
    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;

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

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

    настоящий ответ:
    [0] := $fffffffffffffffe;
    [1] := $ffffffffffffffff;
    [2] := $ffffffffffffffff;
    [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-команды.
 
Конференция "Начинающим" » Работа с внешним массивом внутри функции переданным как параметр
Есть новые Нет новых   [118241   +25][b:0.001][p:0.002]