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

[擂台] 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-11-15 09:49 , Processed in 0.027360 second(s), 15 queries .

Powered by Discuz! X3.5

© 2001-2024 Discuz! Team.

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