找回密码
 欢迎注册
楼主: 无心人

[擂台] x86上128位二进制乘法最快速算法征解

[复制链接]
 楼主| 发表于 2008-3-31 11:37:31 | 显示全部楼层
那你遇到的第一个问题就是寄存器不足
毋因群疑而阻独见  毋任己意而废人言
毋私小惠而伤大体  毋借公论以快私情
发表于 2008-3-31 13:15:02 | 显示全部楼层
  寄存器不足就得使用RAM了,其实,如果这个问题改为 256bit × 256bit,所有的版本都会遇到寄存器不足的问题。
  下面是一个完全不使用 任何MMX,SSE,SSE2指令的版本,共102条指令。 在我的电脑上进行测试,结果表明,除了76楼(fixed bug后为96楼)的程序,比这个程序快2倍以上以外,其他的版本 的加速程度 都无法达到2倍。
  先贴出测试结果。
Test function: UInt128x128To256_ANSI_C32(..) 10000000 times...
Elapsed time: 1905.637 ms
  EAAC506E 17E8573A 5A8A540B ACAE5D9E * C30C395E 93C433B8 4AC06050 7E9A50E8
= B2CC75EE B201F29B 1CDA40BB 312C399D   A214AF3C 4BD8278E D1BC04E3 10523730

Test function: UInt128x128To256_SSE2_40F(..) 10000000 times...
Elapsed time: 706.421 ms
  EAAC506E 17E8573A 5A8A540B ACAE5D9E * C30C395E 93C433B8 4AC06050 7E9A50E8
= B2CC75EE B201F29B 1CDA40BB 312C399D   A214AF3C 4BD8278E D1BC04E3 10523730

Test function: UInt128x128To256_SSE2_42F(..) 10000000 times...
Elapsed time: 729.128 ms
  EAAC506E 17E8573A 5A8A540B ACAE5D9E * C30C395E 93C433B8 4AC06050 7E9A50E8
= B2CC75EE B201F29B 1CDA40BB 312C399D   A214AF3C 4BD8278E D1BC04E3 10523730

Test function: UInt128x128To256_SSE2_54F(..) 10000000 times...
Elapsed time: 695.204 ms
  EAAC506E 17E8573A 5A8A540B ACAE5D9E * C30C395E 93C433B8 4AC06050 7E9A50E8
= B2CC75EE B201F29B 1CDA40BB 312C399D   A214AF3C 4BD8278E D1BC04E3 10523730

Test function: UInt128x128To256_SSE2_56F(..) 10000000 times...
Elapsed time: 580.515 ms
  EAAC506E 17E8573A 5A8A540B ACAE5D9E * C30C395E 93C433B8 4AC06050 7E9A50E8
= B2CC75EE B201F29B 1CDA40BB 312C399D   A214AF3C 4BD8278E D1BC04E3 10523730

Test function: UInt128x128To256_SSE2_58F(..) 10000000 times...
Elapsed time: 880.662 ms
  EAAC506E 17E8573A 5A8A540B ACAE5D9E * C30C395E 93C433B8 4AC06050 7E9A50E8
= B2CC75EE B201F29B 1CDA40BB 312C399D   A214AF3C 4BD8278E D1BC04E3 10523730

Test function: UInt128x128To256_SSE2_69F(..) 10000000 times...
Elapsed time: 834.156 ms
  EAAC506E 17E8573A 5A8A540B ACAE5D9E * C30C395E 93C433B8 4AC06050 7E9A50E8
= B2CC75EE B201F29B 1CDA40BB 312C399D   A214AF3C 4BD8278E D1BC04E3 10523730

Test function: UInt128x128To256_SSE2_94F(..) 10000000 times...
Elapsed time: 864.124 ms
  EAAC506E 17E8573A 5A8A540B ACAE5D9E * C30C395E 93C433B8 4AC06050 7E9A50E8
= B2CC75EE B201F29B 1CDA40BB 312C399D   A214AF3C 4BD8278E D1BC04E3 10523730

Test function: UInt128x128To256_SSE2_96F(..) 10000000 times...
Elapsed time: 403.787 ms
  EAAC506E 17E8573A 5A8A540B ACAE5D9E * C30C395E 93C433B8 4AC06050 7E9A50E8
= B2CC75EE B201F29B 1CDA40BB 312C399D   A214AF3C 4BD8278E D1BC04E3 10523730

Test function: UInt128x128To256_ALU_102F(..) 10000000 times...
Elapsed time: 945.073 ms
  EAAC506E 17E8573A 5A8A540B ACAE5D9E * C30C395E 93C433B8 4AC06050 7E9A50E8
= B2CC75EE B201F29B 1CDA40BB 312C399D   A214AF3C 4BD8278E D1BC04E3 10523730

再贴出完整的代码.
  1. _declspec(naked)
  2. void UInt128x128To256_ALU_102F( UINT32 * const result,
  3.                                 const UINT32 * const left,
  4.                                 const UINT32 * const right )

  5. {
  6.      #undef LEFT_REG
  7.      #undef RIGHT_REG
  8.      #undef RESULT_REG

  9.      #define LEFT_REG        esi
  10.      #define RIGHT_REG        edi
  11.      #define RESULT_REG        ebp

  12.      __asm
  13.     {
  14.         push        esi
  15.         push        edi
  16.         push        ebx
  17.         push        ebp


  18.         mov        RIGHT_REG, dword ptr[esp +  0Ch+16]   ;  right
  19.         mov        LEFT_REG,  dword ptr [esp + 08h+16]   ; left
  20.         mov        RESULT_REG, dword ptr[esp + 04h+16]   ; result
  21.         xor        ebx, ebx
  22.         xor        ecx, ecx

  23.         // result[0],一次乘法
  24.         mov        eax,dword ptr [LEFT_REG]
  25.         mul        dword ptr [RIGHT_REG]
  26.         mov        dword ptr [RESULT_REG], eax
  27.         mov        dword ptr [RESULT_REG+4], edx


  28.         // result[1],2次乘法
  29.         mov        eax,dword ptr [LEFT_REG]
  30.         mul        dword ptr [RIGHT_REG+4]
  31.         add        dword ptr [RESULT_REG+4], eax
  32.         adc        ecx, edx
  33.         adc        ebx,0

  34.         mov        eax,dword ptr [LEFT_REG+4]
  35.         mul        dword ptr [RIGHT_REG]
  36.         add        dword ptr [RESULT_REG+4], eax
  37.         adc        ecx, edx
  38.         adc        ebx,0

  39.         mov        dword ptr [RESULT_REG+8], ecx
  40.         mov        ecx, ebx


  41.         // result[2], 3 次乘法
  42.         xor        ebx, ebx
  43.         mov        eax,dword ptr [LEFT_REG]
  44.         mul        dword ptr [RIGHT_REG+8]
  45.         add        dword ptr [RESULT_REG+8], eax
  46.         adc        ecx,edx
  47.         adc        ebx,0

  48.         mov        eax,dword ptr [LEFT_REG+4]
  49.         mul        dword ptr [RIGHT_REG+4]
  50.         add        dword ptr [RESULT_REG+8], eax
  51.         adc        ecx, edx
  52.         adc        ebx,0

  53.         mov        eax,dword ptr [LEFT_REG+8]
  54.         mul        dword ptr [RIGHT_REG+0]
  55.         add        dword ptr [RESULT_REG+8], eax
  56.         adc        ecx, edx
  57.         adc        ebx,0

  58.         mov        dword ptr [RESULT_REG+12], ecx
  59.         mov        ecx, ebx


  60.         // result[3],4次乘法
  61.         xor        ebx, ebx
  62.         mov        eax,dword ptr [LEFT_REG+0]
  63.         mul        dword ptr [RIGHT_REG+12]
  64.         add        dword ptr [RESULT_REG+12], eax
  65.         adc        ecx, edx
  66.         adc        ebx,0

  67.         mov        eax,dword ptr [LEFT_REG+4]
  68.         mul        dword ptr [RIGHT_REG+8]
  69.         add        dword ptr [RESULT_REG+12], eax
  70.         adc        ecx, edx
  71.         adc        ebx,0

  72.         mov        eax,dword ptr [LEFT_REG+8]
  73.         mul        dword ptr [RIGHT_REG+4]
  74.         add        dword ptr [RESULT_REG+12], eax
  75.         adc        ecx, edx
  76.         adc        ebx,0


  77.         mov        eax,dword ptr [LEFT_REG+12]
  78.         mul        dword ptr [RIGHT_REG+0]
  79.         add        dword ptr [RESULT_REG+12], eax
  80.         adc        ecx, edx
  81.         adc        ebx,0

  82.         mov        dword ptr [RESULT_REG+16], ecx
  83.         mov        ecx, ebx


  84.         // result[4],3次乘法
  85.         xor        ebx, ebx
  86.         mov        eax,dword ptr [LEFT_REG+4]
  87.         mul        dword ptr [RIGHT_REG+12]
  88.         add        dword ptr [RESULT_REG+16], eax
  89.         adc        ecx, edx
  90.         adc        ebx,0

  91.         mov        eax,dword ptr [LEFT_REG+8]
  92.         mul        dword ptr [RIGHT_REG+8]
  93.         add        dword ptr [RESULT_REG+16], eax
  94.         adc        ecx, edx
  95.         adc        ebx,0

  96.         mov        eax,dword ptr [LEFT_REG+12]
  97.         mul        dword ptr [RIGHT_REG+4]
  98.         add        dword ptr [RESULT_REG+16], eax
  99.         adc        ecx, edx
  100.         adc        ebx,0
  101.         mov        dword ptr [RESULT_REG+20], ecx
  102.         mov        ecx, ebx


  103.         // result[5],2次乘法
  104.         xor        ebx, ebx
  105.         mov        eax,dword ptr [LEFT_REG+8]
  106.         mul        dword ptr [RIGHT_REG+12]
  107.         add        dword ptr [RESULT_REG+20], eax
  108.         adc        ecx, edx
  109.         adc        ebx,0

  110.         mov        eax,dword ptr [LEFT_REG+12]
  111.         mul        dword ptr [RIGHT_REG+8]
  112.         add        dword ptr [RESULT_REG+20], eax
  113.         adc        ecx, edx
  114.         adc        ebx,0


  115.         // result[6,7],1次乘法
  116.         mov        eax,dword ptr [LEFT_REG+12]
  117.         mul        dword ptr [RIGHT_REG+12]
  118.         add        ecx, eax
  119.         adc        ebx,edx

  120.         mov        dword ptr [RESULT_REG+24], ecx
  121.         mov        dword ptr [RESULT_REG+28], ebx


  122.         pop        ebp
  123.         pop        ebx
  124.         pop        edi
  125.         pop        esi

  126.         ret
  127.      }
  128. }
复制代码
毋因群疑而阻独见  毋任己意而废人言
毋私小惠而伤大体  毋借公论以快私情
 楼主| 发表于 2008-3-31 13:48:49 | 显示全部楼层
可以做到不用额外单元就不错了
===========================
256X256似乎没必要写了

只要用MMX寄存器或者SSE寄存器
可以做到不用内存保存临时变量的
除非超过2^31个双字乘
=============================
之所以加速比达不到2,关键是乘法本身速度慢
进位加法复杂
毋因群疑而阻独见  毋任己意而废人言
毋私小惠而伤大体  毋借公论以快私情
发表于 2008-3-31 13:56:04 | 显示全部楼层
我的代码只用了四个push 语句 保存一些寄存器的旧的值。除此以外,并没有使用额外的临时变量。
毋因群疑而阻独见  毋任己意而废人言
毋私小惠而伤大体  毋借公论以快私情
 楼主| 发表于 2008-3-31 21:42:51 | 显示全部楼层
折腾来折腾去
还是Knuth算法最好
毋因群疑而阻独见  毋任己意而废人言
毋私小惠而伤大体  毋借公论以快私情
 楼主| 发表于 2008-3-31 21:49:09 | 显示全部楼层
void  AsmMulLL(unsigned long  *pL, unsigned long  *pR, unsigned long  *pA, unsigned long tL, unsigned long tR)
{
if ((tL == 0) || (tR == 0))
   return;
__asm          {
             mov ecx, tL
             mov  esi, dword ptr [pL]
             mov  edi, dword ptr [pR]
             mov  ebx, dword ptr [pA]
             pxor mm3, mm3

mbinmul2:
             mov edx, ecx
             mov eax, ebx
             pxor mm0, mm0
             mov ecx, tR                         
             movd mm1, dword ptr [esi]
             movd mm4, edi
mbinmul3:
             movd mm2, dword ptr [edi]
             lea edi, [edi+4]
             movd mm3, dword ptr [ebx]                         
             pmuludq mm2, mm1
             paddq mm0, mm3
             paddq mm0, mm2
             movd dword ptr [ebx], mm0
             psrlq mm0, 32
             lea ebx, [ebx+4]
             loop mbinmul3
             movd edi, mm4
             movd dword ptr [ebx], mm0
             mov ebx, eax
             lea esi, [esi+4]
             lea ebx, [ebx+4]
             mov ecx, edx
             loop mbinmul2
             emms
             }
}
做个备份
到学校去测试
毋因群疑而阻独见  毋任己意而废人言
毋私小惠而伤大体  毋借公论以快私情
 楼主| 发表于 2008-4-1 08:12:36 | 显示全部楼层
void UInt128x128To256_SSE2_107F( UINT32 * const result,
                                const UINT32 * const left,
                                const UINT32 * const right )
{
__asm          {
             mov ecx, 4
             mov  esi, dword ptr [left]
             mov  edi, dword ptr [right]
             mov  ebx, dword ptr [result]
             pxor xmm0, xmm0
             pxor xmm1, xmm1
             movdqa xmmword ptr [ebx], xmm0
             movdqa xmmword ptr [ebx+16], xmm1   
             pxor mm3, mm3
mbinmul2:
             mov edx, ecx
             mov eax, ebx
             pxor mm0, mm0
             mov ecx,  4                       
             movd mm1, dword ptr [esi]
             movd mm4, edi
mbinmul3:
             movd mm2, dword ptr [edi]
             lea edi, [edi+4]
             movd mm3, dword ptr [ebx] //这里存在问题                        
             pmuludq mm2, mm1
             paddq mm0, mm3
             paddq mm0, mm2
             movd dword ptr [ebx], mm0
             psrlq mm0, 32
             lea ebx, [ebx+4]
             loop mbinmul3
             movd edi, mm4
             movd dword ptr [ebx], mm0
             mov ebx, eax
             lea esi, [esi+4]
             lea ebx, [ebx+4]
             mov ecx, edx
             loop mbinmul2
             emms
             }
}

改成本题要求看下时间 :)
//在我这里时间和40差不多
毋因群疑而阻独见  毋任己意而废人言
毋私小惠而伤大体  毋借公论以快私情
 楼主| 发表于 2008-4-1 10:38:18 | 显示全部楼层
//各种优化均尝试了,lea改add,loop改双指令,均无效
//edi压栈保存造成时间增加
但如果改普遍的,需额外执行一个结果清零的函数
目前问题
1、是否能把edi保存在常规寄存器里,就是再节约一个寄存器
2、能否在不增加很多代码情况下,去掉结果预清零过程
解决这两个问题,俺的B计划之长乘将提前出笼

假设m个双字乘以n个双子
该算法的执行指令数约等于13m + 10mn
毋因群疑而阻独见  毋任己意而废人言
毋私小惠而伤大体  毋借公论以快私情
发表于 2008-4-1 11:26:47 | 显示全部楼层
楼主可能是想 找出一个解决长乘 算法的完美算法。这基本是徒劳的。

   最根本的解决之道是:大数的表示法不能采用232进制,而转为采用 $2^31$或者 $2^30$进制,即采用每个DWORD存储31位或者30位2进制数,虽然浪费了一点存储空间,也带来乘法次数的增多(增加了大约1/15多一点),但其优点可完全抵消此缺点。并对算法的设计带来实质性的帮助。
  对于使用ALU指令来说,可大大降低ADC指令的运行次数。
  对于使用SSE2指令来说,可在ALU指令的基础上真正提速2倍,充分发挥SSE2指令 1次可计算2个32bit×32bit  和 64bbit + 64bit的能力。平均而言,每次30 bit × 30bit的运算仅需要使用3条指令。
   
     举例来说,以那个128bit*128bit 的 函数为例,需要 4×4次 32bit × 32bit 的乘法,平均每次 32bit × 32bit 的乘 需要使用 6条以上的指令,以此推算,一个15DWORD( 15×32bit) × 15DWORD 的乘法 需要 15×15×6= 1350次 指令。  而依照本方法,采用2^30进制,一个15×32bit的数需要 16个DWORD 存储,故采用此方式存储,需要16×16次 30bit* 30bit的乘法,所以需要 16*16*3=768条指令,相比前者的1350条指令,单指令数而言,减少至先前的57%,若以指令数和运行时间成反比(即假定每条指令的时间均相同),则此法可提速75%
毋因群疑而阻独见  毋任己意而废人言
毋私小惠而伤大体  毋借公论以快私情
 楼主| 发表于 2008-4-1 11:42:04 | 显示全部楼层
那你并没看到解包运算的麻烦吧
你怎么解释不在字节倍数上断开
而造成结果后处理的工作量?
===========================
而且,不光是乘法
加法也是常见运算
这么表示并不能在加上取得任何效果
毋因群疑而阻独见  毋任己意而废人言
毋私小惠而伤大体  毋借公论以快私情
您需要登录后才可以回帖 登录 | 欢迎注册

本版积分规则

小黑屋|手机版|数学研发网 ( 苏ICP备07505100号 )

GMT+8, 2024-5-2 05:10 , Processed in 0.049678 second(s), 15 queries .

Powered by Discuz! X3.5

© 2001-2024 Discuz! Team.

快速回复 返回顶部 返回列表