gpt4 book ai didi

delphi - 使用指向数组中的两个 UInt64 加载 xmm 寄存器

转载 作者:行者123 更新时间:2023-12-01 18:53:06 27 4
gpt4 key购买 nike

我正在尝试加载 128 位 xmm注册两个 UInt64 Delphi (XE6) 中的整数。

背景

XMM 寄存器是 128 位的,可以加载多个独立的整数。然后,您可以让 CPU 并行添加这些多个整数。

例如,您可以加载 xmm0 和 xmm1 各有四个 UInt32,然后让 CPU 同时添加所有四对。

xmm0: $00001000 $00000100 $00000010 $00000001
+ + + +
xmm1: $00002000 $00000200 $00000020 $00000002
= = = =
xmm0: $00003000 $00000300 $00000030 $00000003

加载 xmm0 和 xmm0 后,使用以下命令执行四对的相加:
paddd xmm0, xmm1    //Add packed 32-bit integers (i.e. xmm0 := xmm0 + xmm1)

您也可以使用 8 x 16 位整数来实现:
xmm0: $001F $0013 $000C $0007 $0005 $0003 $0002 $0001
+ + + + + + + +
xmm1: $0032 $001F $0013 $000C $0007 $0005 $0003 $0002
= = = = = = = =
xmm0: $0051 $0032 $001F $0013 $000C $0007 $0005 $0003

随着指示
paddw xmm0, xmm1  //Add packed 16-bit integers

现在对于 64 位整数

加载两个 64 位整数到 xmm注册,你必须使用:
  • movdqu:移动双四字(未对齐)
  • movdqa:移动双四字(对齐)

  • 在这个简单的例子中,我们不用担心 UInt64 是否对齐,我们将简单地使用未对齐的版本 ( movdqu)

    我们首先要处理的是 Delphi 编译器 知道 movdqu 需要一个 128 位的东西来加载 - 它正在加载 四字。

    为此,我们将创建一个 128 位结构,它也可以很好地让我们处理两个 64 位值:
    TDoubleQuadword = packed record
    v1: UInt64; //value 1
    v2: UInt64; //value 2
    end;

    现在我们可以在测试控制台应用程序中使用这种类型:
    procedure Main;
    var
    x, y: TDoubleQuadword;
    begin
    //[1,5] + [2,7] = ?
    x.v1 := $0000000000000001;
    x.v2 := $0000000000000005;

    y.v1 := $0000000000000002;
    y.v2 := $0000000000000007;

    asm
    movdqu xmm0, x //move unaligned double quadwords (xmm0 := x)
    movdqu xmm1, y //move unaligned double quadwords (xmm1 := y)

    paddq xmm0, xmm1 //add packed quadword integers (xmm0 := xmm0 + xmm1)

    movdqu x, xmm0 //move unaligned double quadwords (x := xmm0)

    end;

    WriteLn(IntToStr(x.v1)+', '+IntToSTr(x.v2));
    end;

    这有效,打印出来:
    3, 12

    关注奖品

    着眼于使 x 和 y 对齐的目标(但不是我的问题的必要部分),假设我们有一个 指针 TDoubleQuadword结构体:
    TDoubleQuadword = packed record
    v1: UInt64; //value 1
    v2: UInt64; //value 2
    end;
    PDoubleQuadword = ^TDoubleQuadword;

    我们现在将假设的测试函数更改为使用 PDoubleQuadword :
    procedure AlignedStuff;
    var
    x, y: PDoubleQuadword;
    begin
    x := GetMemory(sizeof(TDoubleQuadword));
    x.v1 := $0000000000000001;
    x.v2 := $0000000000000005;

    y := GetMemory(sizeof(TDoubleQuadword));
    y.v1 := $0000000000000002;
    y.v2 := $0000000000000007;

    asm
    movdqu xmm0, x //move unaligned double quadwords (xmm0 := x)
    movdqu xmm1, y //move unaligned double quadwords (xmm1 := y)

    paddq xmm0, xmm1 //add packed quadword integers (xmm0 := xmm0 + xmm1)
    movdqu x, xmm0 //move unaligned double quadwords (v1 := xmm0)
    end;

    WriteLn(IntToStr(x.v1)+', '+IntToSTr(x.v2));
    end;

    现在这不能编译,这是有道理的:
    movdqu xmm0, x      //E2107 Operand size mismatch

    这就说得通了。 x论据 必须为 128 位,编译器知道 x实际上只是一个(32 位)指针。

    但它应该是什么?

    现在我们来回答我的问题:它应该是什么?我在键盘上随机捣碎了各种东西,希望编译器之神能接受我的意思。但没有任何效果。
    //Don't try to pass the 32-bit pointer itself, pass the thing it points to:
    movdqu xmm0, x^ //E2107 Operand size mismatch

    //Try casting it
    movdqu xmm0, TDoubleQuadword(x^) //E2105 Inline assembler error

    //i've seen people using square brackets to mean "contents of":
    movdqu xmm0, [x] //E2107 Operand size mismatch

    现在我们放弃理性思考
    movdqu xmm0, Pointer(x)
    movdqu xmm0, Addr(x^)
    movdqu xmm0, [Addr(x^)]
    movdqu xmm0, [Pointer(TDoubleQuadword(x))^]

    我确实得到了一件事要编译:
    movdqu xmm0, TDoubleQuadword(x)

    但当然会加载 地址 x进入寄存器,而不是 x 中的值。

    所以我放弃了。

    完整的最小示例
    program Project3;

    {$APPTYPE CONSOLE}

    {$R *.res}

    uses
    System.SysUtils;

    type
    TDoubleQuadword = packed record
    v1: UInt64; //value 1
    v2: UInt64; //value 2
    end;
    PDoubleQuadword = ^TDoubleQuadword;

    TVectorUInt64 = array[0..15] of UInt64;
    PVectorUInt64 = ^TVectorUInt64;

    procedure AlignedStuff;
    var
    x, y: PVectorUInt64;
    begin
    x := GetMemory(sizeof(TVectorUInt64));
    //x[0] := ...
    //x[1] := ...
    // ...
    //x[3] := ...
    x[4] := $0000000000000001;
    x[5] := $0000000000000005;

    y := GetMemory(sizeof(TVectorUInt64));
    //y[0] := ...
    //y[1] := ...
    // ...
    //y[3] := ...
    y[4] := $0000000000000002;
    y[5] := $0000000000000007;

    asm
    movdqu xmm0, TDoubleQuadword(x[4]) //move unaligned double quadwords (xmm0 := x)
    movdqu xmm1, TDoubleQuadword(y[4]) //move unaligned double quadwords (xmm1 := y)

    paddq xmm0, xmm1 //add packed quadword integers (xmm0 := xmm0 + xmm1)
    movdqu TDoubleQuadword(x[4]), xmm0 //move unaligned double quadwords (v1 := xmm0)
    end;

    WriteLn(IntToStr(x[4])+', '+IntToSTr(x[5]));
    end;

    begin
    try
    AlignedStuff;
    Writeln('Press enter to close...');
    Readln;
    except
    on E: Exception do
    Writeln(E.ClassName, ': ', E.Message);
    end;
    end.

    指针?

    问题询问指针的原因是:
  • 您不能使用堆栈变量(Delphi 不保证堆栈变量的对齐)
  • 你可以将它们复制到一个寄存器中(例如 EAX),但是你正在做一个浪费的复制和函数调用
  • 我已经在内存中对齐了数据

  • 如果我举一个仅涉及添加 UInt64 的代码示例:
    TVectorUInt64 = array[0..15] of UInt64;
    PVectorUInt64 = ^TVectorUInt64;

    var
    v: PVectorUInt64;
    begin
    v := GetMemoryAligned(sizeof(TVectorUInt64), 64); //64-byte alignment

    //v is initalized

    for i := 0 to 15 do
    begin
    v[0] := v[0] + v[4];
    v[1] := v[1] + v[5];
    v[2] := v[2] + v[6];
    v[3] := v[3] + v[7];

    //..and some more changes to v0..v3
    //..and some more changes to v12..v15

    v[8] := v[8] + v[12];
    v[9] := v[9] + v[13];
    v[10] := v[10] + v[14];
    v[11] := v[11] + v[15];

    //...and some more changes to v4..v7

    v[0] := v[0] + v[4];
    v[1] := v[1] + v[5];
    v[2] := v[2] + v[6];
    v[3] := v[3] + v[7];

    //...and some more changes to v0..v3
    //...and some more changes to v12..v15

    v[8] := v[8] + v[12];
    v[9] := v[9] + v[13];
    v[10] := v[10] + v[14];
    v[11] := v[11] + v[15];

    //...and some more changes to v4..v7

    v[0] := v[0] + v[4];
    v[1] := v[1] + v[5];
    v[2] := v[2] + v[6];
    v[3] := v[3] + v[7];

    //..and some more changes to v0..v3
    //..and some more changes to v12..v15

    v[8] := v[8] + v[12];
    v[9] := v[9] + v[13];
    v[10] := v[10] + v[14];
    v[11] := v[11] + v[15];

    //...and some more changes to v4..v7

    v[0] := v[0] + v[4];
    v[1] := v[1] + v[5];
    v[2] := v[2] + v[6];
    v[3] := v[3] + v[7];

    //...and some more changes to v0..v3
    //...and some more changes to v12..v15

    v[8] := v[8] + v[12];
    v[9] := v[9] + v[13];
    v[10] := v[10] + v[14];
    v[11] := v[11] + v[15];

    //...and some more changes to v4..v7
    end;

    从概念上讲,将代码更改为:
          //v[0] := v[0] + v[4];
    //v[1] := v[1] + v[5];
    asm
    movdqu xmm0, v[0]
    movdqu xmm1, v[4]
    paddq xmm0, xmm1
    movdqu v[0], xmm0
    end
    //v[2] := v[2] + v[6];
    //v[3] := v[3] + v[7];
    asm
    movdqu xmm0, v[2]
    movdqu xmm1, v[6]
    paddq xmm0, xmm1
    movdqu v[2], xmm0
    end

    //v[8] := v[8] + v[12];
    //v[9] := v[9] + v[13];
    asm
    movdqu xmm0, v[8]
    movdqu xmm1, v[12]
    paddq xmm0, xmm1
    movdqu v[8], xmm0
    end
    //v[10] := v[10] + v[14];
    //v[11] := v[11] + v[15];
    asm
    movdqu xmm0, v[10]
    movdqu xmm1, v[14]
    paddq xmm0, xmm1
    movdqu v[10], xmm0
    end

    诀窍是让 Delphi 编译器接受它。
  • 它适用于即时数据
  • 指向数据的指针失败
  • 你会想[contentsOfSquareBrackets]会工作

  • 奖金喋喋不休

    使用 David 的解决方案(函数调用开销)导致性能提高 -7%(90 MB/s -> 83 MB/s 的算法吞吐量)

    看起来,在 XE6 编译器中,概念上调用是有效的:
    movdqu xmm0, TPackedQuadword

    但是编译器只是没有大脑让您执行概念调用:
    movdqu xmm0, PPackedQuadword^

    或者这是道德上的等价物。

    如果这就是答案,请不要害怕。拥抱它,并把它作为答案的形式:

    *"The compiler does not support dereferencing a pointer inside an asm block. No matter if you try that with a caret (^), or square brackets ([...]). It just cannot be done.



    如果这就是答案:回答它。

    如果不是这样,编译器 可以支持 asm 中的指针阻止,然后发布答案。

    最佳答案

    Delphi 中的内联汇编程序的文档没有应有的全面,而且很多功能根本没有记录。所以我不能确定这一点,但据我所知,根本不支持您尝试编写的汇编语句,其中一个操作数是指针类型的局部变量。

    我强烈建议您避免在同一个函数中混用 Pascal 代码和汇编代码。当你在同一个函数中的 Pascal 代码和汇编代码之间移动时,它很难生成高效的代码,并且很难管理寄存器的使用。

    我个人规定永远不要混合使用 Pascal 和内联汇编程序。始终编​​写纯汇编函数。例如,对于 32 位代码,您将编写一个完整的程序,如下所示:

    {$APPTYPE CONSOLE}

    type
    PDoubleQuadword = ^TDoubleQuadword;
    TDoubleQuadword = record
    v1: UInt64;
    v2: UInt64;
    end;

    function AddDoubleQuadword(const dqw1, dqw2: TDoubleQuadword): TDoubleQuadword;
    asm
    movdqu xmm0, [eax]
    movdqu xmm1, [edx]
    paddq xmm0, xmm1
    movdqu [ecx], xmm0
    end;

    procedure AlignedStuff;
    var
    x, y: PDoubleQuadword;
    begin
    New(x);
    x.v1 := $0000000000000001;
    x.v2 := $0000000000000005;

    New(y);
    y.v1 := $0000000000000002;
    y.v2 := $0000000000000007;

    x^ := AddDoubleQuadword(x^, y^);

    Writeln(x.v1, ', ', x.v2);
    end;

    begin
    AlignedStuff;
    Readln;
    end.

    该程序输出:
    3, 12

    Or you could use a record with operators:

    type
    PDoubleQuadword = ^TDoubleQuadword;
    TDoubleQuadword = record
    v1: UInt64;
    v2: UInt64;
    class operator Add(const dqw1, dqw2: TDoubleQuadword): TDoubleQuadword;
    end;

    class operator TDoubleQuadword.Add(const dqw1, dqw2: TDoubleQuadword): TDoubleQuadword;
    asm
    movdqu xmm0, [eax]
    movdqu xmm1, [edx]
    paddq xmm0, xmm1
    movdqu [ecx], xmm0
    end;

    然后在调用站点你有:
    x^ := x^ + y^;

    关于delphi - 使用指向数组中的两个 UInt64 加载 xmm 寄存器,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53501137/

    27 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com