【问题标题】:Inline assembly inefficient procedure in Delphi using SSE2Delphi中使用SSE2的内联汇编效率低下的过程
【发布时间】:2011-10-26 21:26:16
【问题描述】:

我有一个简单的基于浮点的操作,它总是执行两次。所以我试图将它翻译成 SSE,但它失败了。高级语言是 Delphi,所以它不支持 Intrinsics 函数,我必须编写整个东西。 基本上我只有参数加载/卸载和一些乘法和加法。:

Procedure TLP1Poly2.Process(Const _a1, _b1, _OldIn1, _OldIn2, _OldOut1, _OldOut2:     Double; Var Sample1, Sample2: Double);
Asm
  MOVLPD  XMM4, _a1
  MOVHPD  XMM4, _a1
  MOVLPD  XMM3, _b1
  MOVHPD  XMM3, _b1
  //
  MOVLPD  XMM0, [Sample1]
  MOVHPD  XMM0, [Sample2]
  MULPD   XMM0, XMM4
  //
  MOVLPD  XMM1, _OldIn1
  MOVHPD  XMM1, _OldIn2
  MULPD   XMM1, XMM4
  //
  MOVLPD  XMM2, _OldOut1
  MOVHPD  XMM2, _OldOut2
  MULPD   XMM2, XMM3
  //
  ADDPD   XMM0, XMM1
  ADDPD   XMM0, XMM2
  //
  MOVLPD  [Sample1], XMM0
  MOVHPD  [Sample2], XMM0
  //
  // which stands for twice this:
  // Sample:= Sample*a1 + oldinp*a1 + oldout*b1;
  // 
End;

但此过程不起作用,如果我在 Sample1/Sample2 加载/保存之间“nop”所有内容,则可以,否则我的过滤器将静音。我在 SSE 中没有得到什么基本的东西?

附录:

老班班:

constructor TLP1.create;
begin
  oldfreq := -1 ;
end;
procedure TLp1.process(inp,Frq,SR :single);
begin
  if Frq<>oldfreq then
    begin
      a := 2* SR;
      t := Frq * _ppi;
      n := 1/ (a+t) ;
      b1:= (a - t) * n;
  a1:= t * n;
  oldfreq := frq;
    end;
   outlp   := (inp+_kd)*a1 + oldinp*a1 + oldout*b1;
   oldout  := outlp ;
   oldinp  := inp;
 end;

新课程:

Procedure TLP2Poly2.SetSamplerate(Const Value: Single);
Begin
  If Value = FSamplerate Then Exit;
  FSamplerate := Value;
  UpdateCoefficients;
End;

Procedure TLP2Poly2.SetFrequency(Const Value: Single);
Begin
 If Value = FFrequency Then Exit;
  FFrequency := Value;
  UpdateCoefficients;
End;

Procedure TLP2Poly2.UpdateCoefficients;
Var
  a,t,n: Single;
Begin
  a := 2 * FSamplerate ;
  t := FFrequency * 2 * pi;
  n := 1/ (a+t) ;
  b1:= (a - t) * n;
  a1:= t * n;
End;

Procedure TLP2Poly2.Process(Var Sample1, Sample2: Double);
Var
  o1, o2: Double;
Begin
  o1 := Sample1;
  o2 := Sample2;
  IntProcess( a1, b1, OldIn1, OldIn2, OldOut1, OldOut2, Sample1, Sample2);
  OldOut1 := Sample1;
  OldOut2 := Sample2;
  OldIn1  := o1;
  OldIn2  := o2;
End;

Procedure TLP2Poly2.IntProcess(Const _a1, _b1, _OldIn1, _OldIn2, _OldOut1, _OldOut2:    Double; Var Sample1, Sample2: Double);
Asm
  MOVLPD  XMM4, _a1
  MOVHPD  XMM4, _a1
  MOVLPD  XMM3, _b1
  MOVHPD  XMM3, _b1
  //
  MOVLPD  XMM0, [Sample1]
  MOVHPD  XMM0, [Sample2]
  MULPD   XMM0, XMM4
  //
  MOVLPD  XMM1, _OldIn1
  MOVHPD  XMM1, _OldIn2
  MULPD   XMM1, XMM4
  //
  MOVLPD  XMM2, _OldOut1
  MOVHPD  XMM2, _OldOut2
  MULPD   XMM2, XMM3
  //
  ADDPD   XMM0, XMM1
  ADDPD   XMM0, XMM2
  //
  MOVLPD  [Sample1], XMM0
  MOVHPD  [Sample2], XMM0
End;

【问题讨论】:

  • “它不起作用”并不意味着什么。这段代码是什么?它应该做什么,而不是做什么?更详细一点会很有帮助。
  • 这是一个低通滤波器,1 极。原始代码作为注释放在突出显示的代码的末尾。 a1 和 b1 是系数,与滤波器的原始版本相同。
  • 你要翻译的到底是什么Delphi程序?您的代码备注不足。
  • 我觉得注释的代码就够了。
  • @az01:如果您在此处需要帮助,您是否认为注释代码就足够了。如果“注释代码足够”,请使用它自己解决问题。如果您在此处需要帮助,而人们要求提供更多信息,请将其提供给他们(或删除您的问题)。粗鲁和不合作不会让你得到帮助。 :)

标签: delphi assembly x86 sse


【解决方案1】:

在为 Delphi 编写汇编程序时,尤其是在 64 位模式下,您应该始终注意参数是如何传递的。我从不使用前 4 个参数的名称,因为它们无论如何都在寄存器中。我直接使用这些寄存器。

请注意,_a1、_b1、_oldIn1 和 _oldIn2 分别在 XMM0 - XMM3 中传递,因此您的代码的第一部分会覆盖其中一些寄存器。例如,用_b1 加载XMM3 会覆盖_oldIn2。 XMM2 也是如此,它拥有 _oldIn1。

重新安排寄存器的使用是有意义的,这样您就不必使用内存作为中间存储。

IOW,尝试类似(未经测试):

asm
        MOVDDUP XMM0,XMM0
        MOVDDUP XMM1,XMM1

        MOVLPD  XMM4,[Sample1]
        MOVHPD  XMM4,[Sample2]
        MULPD   XMM4,XMM0

        // etc...

【讨论】:

  • 感谢您的建议。而且我不确定 MOVDUP 是否正常。最大的问题是我使用一些旧的离线文档来学习SSE(softpixel.com/~cwright/programming/simd)。
  • 试试 AMD 或 Intel 的官方文档。它们可以作为多个 .pdf 文件免费下载。
  • 要分两半加载,请以movsd 开头,而不是movlpd。如果您想要合并到旧值并依赖它,请仅使用movlpd。另外上半部分也可以是movlps,以节省一个字节的代码大小。
【解决方案2】:

在 Delphi 中有一个调试器窗格(“FPU”),它显示了 SSE 寄存器。因此,如果您为过滤器提供一些非零值,您应该能够找到静默输出的来源。

【讨论】:

    猜你喜欢
    • 2012-02-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-03-24
    • 2012-02-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多