加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 大数据 > 正文

delphi – 如何在64位代码中实现高效的32位DivMod

发布时间:2020-12-15 10:06:34 所属栏目:大数据 来源:网络整理
导读:我想使用一个专门在32位操作数上运行的DivMod函数. implementation in the RTL以16位变量返回值.它的声明是: procedure DivMod(Dividend: Cardinal; Divisor: Word; var Result,Remainder: Word); 所以,我不能使用它,因为我的输入可能溢出返回值. 朴素的Pas
我想使用一个专门在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,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,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;

解决方法

对于总是除以10(每条评论)的特殊情况,您可以执行以下操作:
procedure DivMod10(num : Cardinal; var q,r : Cardinal); inline;
var
  rl : uInt64;
begin
  rl := UInt64(3435973837)*num;
  q := rl shr 35;
  r := num - q*10;
end;

算法根据分母而有所不同,但确定它的来源和幻数可以在libdivide中找到.这对于所有无符号32位整数都是准确的,并且比使用div快3倍(并提供余数) .

基准(优化):

t0 := GetTickCount;
  for I := 1 to 999999999 do begin
    DivMod10(i,q,r);
  end;
  ShowMessage(IntToStr(GetTickCount - t0));  // result :  1809

  t0 := GetTickCount;
  for I := 1 to 999999999 do begin
    q := i div 10;
  end;
  ShowMessage(IntToStr(GetTickCount - t0));  // result :  5336

测试:

for I := 1 to High(Cardinal) do begin
  DivMod10(i,r);
  if q <> (i div 10) then WriteLn(IntToStr(i));
  // no mismatch found
end;

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读