【问题标题】:Combining ASM with non-asm code (or SwapInt64 ASM function needed)将 ASM 与非 asm 代码相结合(或需要 SwapInt64 ASM 函数)
【发布时间】:2015-10-18 11:39:13
【问题描述】:

我需要处理来自旧 Mac 时代(旧摩托罗拉 CPU)的文件。字节是大端的,所以我有一个将 Int64 交换为英特尔小端的功能。该功能是 ASM,适用于 32 位 CPU,但不适用于 64。对于 64 位,我有一个不是 ASM 的不同功能。我想使用 IFDEF 组合这些功能。我可以这样做吗?会不会有问题?

interface 

function SwapInt64(Value: Int64): Int64; assembler;  

implementation

{$IFDEF CPUx86}        
function SwapInt64(Value: Int64): Int64; assembler;        { Does not work on 64 bit }                                                                      { 
asm
 MOV     EDX,[DWORD PTR EBP + 12]
 MOV     EAX,[DWORD PTR EBP + 8]
 BSWAP   EAX
 XCHG    EAX,EDX
 BSWAP   EAX
end;

{$else}

 function SwapInt64 (Value: Int64): Int64;
 var P: PInteger;
 begin
  Result: = (Value shl 32) or (Value shr 32);
  P: = @Result;
  P ^: = (Swap (P ^) shl 16) or (Swap (P ^ shr 16));
  Inc (P);
  P ^: = (Swap (P ^) shl 16) or (Swap (P ^ shr 16));
 end;
{$ENDIF}

我认为无论一个是 ASM 而另一个是 Pascal,编译器都会正确编译/调用相应的函数。

【问题讨论】:

  • Mac 也使用小端序。您是否真的在寻找网络来托管功能?为什么你觉得有必要重新实现它们?为什么要使用asm?这不是给你带来麻烦的原因吗?如果您使用 Pascal,您会在家干吗?
  • @DavidHeffernan-对不起。我的意思是“老麦克”。大端(sparc)的那个。
  • @DavidHeffernan - 请参阅与数据来源相关的说明(旧 mac)
  • 除非性能很重要,否则我会使用 Pascal。
  • @DavidHeffernan-ASM 函数应该更快。对?如果可能的话,我想将 asm 保持在 32 位。文件可能很大(1-40GB)。并且所有数据都需要交换!!!!!!! asm 函数可能会提高一点速度。

标签: delphi assembly pascal inline-assembly


【解决方案1】:

你的提议完全没问题。这是一个相当合理的方法。

如果你想在 asm 中进行 64 位交换,对于 x64,这很简单:

function SwapInt64(Value: Int64): Int64;
asm
  MOV    RAX,RCX
  BSWAP  RAX
end;

使用条件将其与 32 位版本结合起来,就像您在问题中所做的那样。

function SwapInt64(Value: Int64): Int64;
{$IF Defined(CPUX86)}
asm
 MOV     EDX,[DWORD PTR EBP + 12]
 MOV     EAX,[DWORD PTR EBP + 8]
 BSWAP   EAX
 XCHG    EAX,EDX
 BSWAP   EAX
end;
{$ELSEIF Defined(CPUX64)}
asm
  MOV    RAX,RCX
  BSWAP  RAX
end;
{$ELSE}
  {$Message Fatal 'Unsupported architecture'}
{$ENDIF}

或者在{$ELSE} 块中包含一个Pascal 实现。

【讨论】:

  • 谢谢大卫。这样就可以了!
【解决方案2】:

如果您追求的是性能,那么在无法内联的单独例程中交换字节的方法有点愚蠢。

假设您有一个数据块并且其中的所有 dword/qwords 需要更改它们的字节顺序,这是一种更好的方法。

这看起来像这样。

对于双字

function SwapDWords(var Data; size: cardinal): boolean;
{ifdef CPUX64}
asm
  //Data in RCX, Size in EDX
  xor EAX,EAX //failure
  test EDX,3 
  jz @MultipleOf4
@error:
  ret
@MultipleOf4
  neg EDX    //Count up instead of down
  jz  @done
  ADD RCX,RDX      
@loop
  mov R8d, [RCX+RDX]
  bswap R8d
  mov [RCX+RDX],R8d  
  add RDX,4   //add is faster than inc on modern processors
  jnz @loop
@done:
  inc EAX  //success
  ret
end;

对于qwords

function SwapQWords(var Data; size: cardinal): boolean;
{ifdef CPUX64}
asm
  //Data in RCX, Size in EDX
  xor EAX,EAX //failure
  test EDX,7 
  jz @MultipleOf8
@error:
  ret
@MultipleOf8
  neg EDX    //Count up instead of down
  jz  @done
  ADD RCX,RDX      
@loop
  mov R8, [RCX+RDX]
  bswap R8
  mov [RCX+RDX],R8
  add RDX,8   //add is faster than inc on modern processors
  jnz @loop
@done:
  inc EAX  //success
  ret
end;

如果您已经使用 64 位,则您拥有 SSE2,并且可以使用 128 位 SSE 寄存器。
现在您可以一次处理 4 个双字,有效地展开循环 4 次。 见:http://www.asmcommunity.net/forums/topic/?id=29743

   movntpd xmm5,[RCX+RDX]  //non-temporal move to avoid polluting the cache
   movdqu xmm0, xmm5
   movdqu xmm1, xmm5
   pxor    xmm5, xmm5
   punpckhbw xmm0, xmm5 ; interleave '0' with bytes of original
   punpcklbw xmm1, xmm5 ;  so they become words
   pshuflw xmm0, xmm0, 27 ; swap the words by shuffling
   pshufhw xmm0, xmm0, 27 ;//27 = B00_01_10_11
   pshuflw xmm1, xmm1, 27
   pshufhw xmm1, xmm1, 27
   packuswb xmm1, xmm0 ; make the words back into bytes.
   movntpd [RCX+RDX], xmm1  //non-temporal move to keep the cache clean.

【讨论】:

  • 传递计数或长度可以让您避免参数有效性检查。至于性能,现在下判断还为时过早。如果缓冲区在磁盘上,那么这很重要。而且没有证据表明数据是同质的。显然,如果它是同质的并且在内存中,那么在 asm 中循环将是最好的。
  • @Johan +1 你的好答案。不幸的是,就我而言,它不起作用。我阅读了一种结构复杂的格式,因此数据是“混合的”。我没有一个双字块。我有各种数据:整数、双字、布尔值、单词等。
  • @Frosty 在这种情况下,我怀疑 asm 与 Pascal 在性能方面有很大不同。碰巧的是,asm 版本实际上更容易编写!尽管您需要编写多个版本。无论如何,决定权在你。
【解决方案3】:

只需使用LEToN() 或BEtoN()

如果数据是小端格式(例如 32 或 64 位 x86 mac、现代 arm),则使用 LE 变体,如果源数据(例如磁盘中的文件)是大端格式,则使用 BE。

根据使用的架构,将内联交换或“无”,通常对于单次转换来说是相当最佳的。对于面向块的解决方案,请参阅发布的 SSE 代码(或 Agner Fog 的)

【讨论】:

  • Leton/beton 是为 Freepascal 而不是 Delphi。我不确定它是否适用于 Delphi。我必须检查一下。
  • 使用 Delphi cpu 定义和假设调用约定可能会在特别是非 Windows FPC 目标上失败。您是否同时使用 asm 函数和 pascal 函数对您的程序进行了基准测试,它真的那么重要吗?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2020-03-22
  • 1970-01-01
  • 2010-11-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多