【发布时间】:2013-11-28 16:11:25
【问题描述】:
我想使用一个专门在 32 位操作数上运行的 DivMod 函数。 implementation in the RTL 返回 16 位变量中的值。它的声明是:
procedure DivMod(Dividend: Cardinal; Divisor: Word; var Result, Remainder: Word);
所以,我不能使用它,因为我的输入可能会溢出返回值。
朴素的 Pascal 实现如下所示:
procedure DivMod(Dividend, Divisor: Cardinal; out Quotient, Remainder: Cardinal);
begin
Quotient := Dividend div Divisor;
Remainder := Dividend mod Divisor;
end;
这很好用,但执行了两次除法。由于该函数是由处于性能瓶颈的部分代码调用的,因此我只想执行一次除法。为此,我从这个问题中使用了 Serg 的 32 位 DivMod:Is there a DivMod that is *not* Limited to Words (<=65535)?
procedure DivMod(Dividend, Divisor: Cardinal; out Quotient, Remainder: Cardinal);
asm
PUSH EBX
MOV EBX,EDX
XOR EDX,EDX
DIV EBX
MOV [ECX],EAX
MOV EBX,Remainder
MOV [EBX],EDX
POP EBX
end;
这很好用。
但现在我想要一个用于 64 位代码的函数版本。请注意,我仍然想对 32 位操作数进行操作,并返回 32 位值。
我应该使用 64 位汇编程序重新编写函数,还是使用 RTL 中的 DivMod 重载并返回 64 位值就足够了?
具体来说,我想知道编写执行 32 位操作的 64 位代码是否有性能优势。这甚至可能吗?还是我会简单地使用UInt64 参数重新实现DivMod 重载?如果值得实现一个定制的 64 位 asm 版本,我将如何去做,注意操作数和操作都是 32 位的。
我认为它看起来像这样,但我不是专家,可能有问题:
procedure DivMod(Dividend, Divisor: Cardinal; out Quotient, Remainder: Cardinal);
asm
MOV EAX,ECX // move Dividend to EAX
MOV ECX,EDX // move Divisor to ECX
XOR EDX,EDX // zeroise EDX
DIV ECX // divide EDX:EAX by ECX
MOV [R8],EAX // save quotient
MOV [R9],EDX // save remainder
end;
【问题讨论】:
-
看起来不错。比 purepascal 版本快大约 33%。
-
@LURD 你知道上面代码中的
DIV ECX是否执行32位操作吗?我理解对了吗?它是否对 64 位值 EDX:EXA 除以 ECX 进行无符号除法?是否值得做 32 位操作而不是完整的 64 位操作? -
将
64-bit标记替换为x86-64标记,因为该问题特定于x86-64,而不是一般的64 位架构。 -
@DavidHeffernan 如果您在编译时知道除数,则整数除法可以加快很多(通过魔术常量和以便宜的
mul交易div和shr)。签出:libdivide.com 还有intel.com/content/dam/www/public/us/en/documents/manuals/… 的第 9.2.4 节,一些编译器会自动执行此操作 - delphi 似乎没有。 -
@J... 0.1 不可表示。您的代码可能更快,但它是错误的。当然。