我在Delphi中编写一个简单的BigInteger类型。它主要由一个动态数组TLimb组成,其中TLimb是一个32位无符号整数,以及一个32位大小的字段,该字段还保存BigInteger的符号位。
要添加两个BigInteger,我创建了一个适当大小的新BigInteger,然后进行了一些记账,然后调用以下过程,向其传递三个指针,分别指向左右操作数及其结果的数组的开头,以及左右肢的数量。
简码:
class procedure BigInteger.PlainAdd(Left, Right, Result: PLimb; LSize, RSize: Integer); asm// EAX = Left, EDX = Right, ECX = Result PUSH ESI PUSH EDI PUSH EBX MOV ESI,EAX // Left MOV EDI,EDX // Right MOV EBX,ECX // Result MOV ECX,RSize // Number of limbs at Left MOV EDX,LSize // Number of limbs at Right CMP EDX,ECX JAE @SkipSwap XCHG ECX,EDX // Left and LSize should be largest XCHG ESI,EDI // so swap@SkipSwap: SUB EDX,ECX // EDX contains rest PUSH EDX // ECX contains smaller size XOR EDX,EDX @MainLoop: MOV EAX,[ESI + CLimbSize*EDX] // CLimbSize = SizeOf(TLimb) = 4. ADC EAX,[EDI + CLimbSize*EDX] MOV [EBX + CLimbSize*EDX],EAX INC EDX DEC ECX JNE @MainLoop POP EDI INC EDI // Do not change Carry Flag DEC EDI JE @LastLimb@RestLoop: MOV EAX,[ESI + CLimbSize*EDX] ADC EAX,ECX MOV [EBX + CLimbSize*EDX],EAX INC EDX DEC EDI JNE @RestLoop@LastLimb:
这段代码很好用,我对此非常满意,直到我注意到,在我的开发设置(iMac上的Parallels VM中为Win7)上,有一个简单的PURE PASCAL加法例程,在用变量和一些if
条款,是更快的比我的平淡,简单的手工制作的汇编程序。
我花了一段时间才发现,在某些CPU(包括我的iMac和较旧的笔记本电脑)上,DEC
or INC
和ADC
or 的组合SBB
可能非常慢。但是,在我的大多数其他计算机上(我还有五台PC可以对其进行测试,尽管其中四台完全相同),但是速度却相当快。
这使我在“慢速”计算机上的代码快了将近三倍,但在“更快”计算机上的代码却慢了约20%。因此,现在,作为初始化代码,我做了一个简单的定时循环,并用它来决定是否将单元设置为调用普通程序或仿真例程。这几乎总是正确的,但是有时它会在应该选择仿真例程的情况下选择(较慢的)普通例程。
但是我不知道这是否是最好的方法。
烙印99