c语言中两个int数相除,c – x86上的两个128位整数的有效乘法/除法...

编译器:MinGW / GCC

问题:不允许使用GPL / LGPL代码(GMP或任何bignum库,对于这个问题来说是过度的,因为我已经实现了这个类).

我构建了自己的128位固定大小的整数类(用于游戏引擎,但可以推广到任何使用情况),我发现当前的乘法和除法运算的性能非常糟糕(是的,我有时间,见下文),我想改进(或改变)执行低级数字运算的算法.

当涉及乘法和除法运算符时,与几乎所有其他类似的运算符相比,它们是无法忍受的.

这些是相对于我自己的计算机的近似测量:

Raw times as defined by QueryPerformanceFrequency:

1/60sec 31080833u

Addition: ~8u

Subtraction: ~8u

Multiplication: ~546u

Division: ~4760u (with maximum bit count)

正如您所看到的,只是进行乘法比加或减慢很多倍.除法比乘法慢10倍.

我想提高这两个运算符的速度,因为每帧可能会进行非常多的计算(点积,各种碰撞检测方法等).

结构(方法省略)看起来有点像:

class uint128_t

{

public:

unsigned long int dw3, dw2, dw1, dw0;

//...

}

乘法目前使用典型的长乘法方法(在汇编中使我可以捕获EDX输出)同时忽略超出范围的单词(也就是说,我只进行10次mull而不是16次).

除法使用移位 – 减法算法(速度取决于操作数的位数).但是,它不是在装配中完成的.我发现有点太难以集合并决定让编译器优化它.

我已经谷歌了几天看着描述算法的页面,例如Karatsuba Multiplication,高基数除法和Newton-Rapson Division,但数学符号有点太过分了.我想使用其中一些高级方法来加速我的代码,但我必须首先将“希腊语”翻译成可理解的东西.

对于那些可能认为我的努力“过早优化”的人;我认为这个代码是一个瓶颈,因为非常基本的数学运算本身变得很慢.我可以在更高级别的代码上忽略这种类型的优化,但是这个代码将被调用/使用到足够重要.

我想建议我应该使用哪种算法来改进乘法和除法(如果可能的话),以及关于建议算法如何工作的基本(希望易于理解)的解释将受到高度赞赏.

编辑:乘以改进

我能够通过将代码内联到operator * =来改进乘法运算,并且它似乎尽可能快.

Updated raw times:

1/60sec 31080833u

Addition: ~8u

Subtraction: ~8u

Multiplication: ~100u (lowest ~86u, highest around ~256u)

Division: ~4760u (with maximum bit count)

这里有一些简单的代码供你检查(注意我的类型名称实际上是不同的,为简单起见,这是编辑的):

//File: "int128_t.h"

class int128_t

{

uint32_t dw3, dw2, dw1, dw0;

// Various constrctors, operators, etc...

int128_t& operator*=(const int128_t& rhs) __attribute__((always_inline))

{

int128_t Urhs(rhs);

uint32_t lhs_xor_mask = (int32_t(dw3) >> 31);

uint32_t rhs_xor_mask = (int32_t(Urhs.dw3) >> 31);

uint32_t result_xor_mask = (lhs_xor_mask ^ rhs_xor_mask);

dw0 ^= lhs_xor_mask;

dw1 ^= lhs_xor_mask;

dw2 ^= lhs_xor_mask;

dw3 ^= lhs_xor_mask;

Urhs.dw0 ^= rhs_xor_mask;

Urhs.dw1 ^= rhs_xor_mask;

Urhs.dw2 ^= rhs_xor_mask;

Urhs.dw3 ^= rhs_xor_mask;

*this += (lhs_xor_mask & 1);

Urhs += (rhs_xor_mask & 1);

struct mul128_t

{

int128_t dqw1, dqw0;

mul128_t(const int128_t& dqw1, const int128_t& dqw0): dqw1(dqw1), dqw0(dqw0){}

};

mul128_t data(Urhs,*this);

asm volatile(

"push %%ebp

\n movl %%eax, %%ebp

\n movl $0x00, %%ebx

\n movl $0x00, %%ecx

\n movl $0x00, %%esi

\n movl $0x00, %%edi

\n movl 28(%%ebp), %%eax #Calc: (dw0*dw0)

\n mull 12(%%ebp)

\n addl %%eax, %%ebx

\n adcl %%edx, %%ecx

\n adcl $0x00, %%esi

\n adcl $0x00, %%edi

\n movl 24(%%ebp), %%eax #Calc: (dw1*dw0)

\n mull 12(%%ebp)

\n addl %%eax, %%ecx

\n adcl %%edx, %%esi

\n adcl $0x00, %%edi

\n movl 20(%%ebp), %%eax #Calc: (dw2*dw0)

\n mull 12(%%ebp)

\n addl %%eax, %%esi

\n adcl %%edx, %%edi

\n movl 16(%%ebp), %%eax #Calc: (dw3*dw0)

\n mull 12(%%ebp)

\n addl %%eax, %%edi

\n movl 28(%%ebp), %%eax #Calc: (dw0*dw1)

\n mull 8(%%ebp)

\n addl %%eax, %%ecx

\n adcl %%edx, %%esi

\n adcl $0x00, %%edi

\n movl 24(%%ebp), %%eax #Calc: (dw1*dw1)

\n mull 8(%%ebp)

\n addl %%eax, %%esi

\n adcl %%edx, %%edi

\n movl 20(%%ebp), %%eax #Calc: (dw2*dw1)

\n mull 8(%%ebp)

\n addl %%eax, %%edi

\n movl 28(%%ebp), %%eax #Calc: (dw0*dw2)

\n mull 4(%%ebp)

\n addl %%eax, %%esi

\n adcl %%edx, %%edi

\n movl 24(%%ebp), %%eax #Calc: (dw1*dw2)

\n mull 4(%%ebp)

\n addl %%eax, %%edi

\n movl 28(%%ebp), %%eax #Calc: (dw0*dw3)

\n mull (%%ebp)

\n addl %%eax, %%edi

\n pop %%ebp

"

:"=b"(this->dw0),"=c"(this->dw1),"=S"(this->dw2),"=D"(this->dw3)

:"a"(&data):"%ebp");

dw0 ^= result_xor_mask;

dw1 ^= result_xor_mask;

dw2 ^= result_xor_mask;

dw3 ^= result_xor_mask;

return (*this += (result_xor_mask & 1));

}

};

至于除法,检查代码是没有意义的,因为我需要改变数学算法以看到任何实质性的好处.唯一可行的选择似乎是高基数除法,但我还没有解决(在我看来)它是如何工作的.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值