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

[讨论] B计划之长加法最佳算法讨论

[复制链接]
 楼主| 发表于 2008-4-16 17:10:58 | 显示全部楼层


知道了
毋因群疑而阻独见  毋任己意而废人言
毋私小惠而伤大体  毋借公论以快私情
发表于 2008-5-4 15:40:57 | 显示全部楼层
增加了2个版本,先给出在PIV 2.6上的测试结果

说明:
   binAdd_base30_ALU_4way_unroll2 为 binAdd_base30_ALU_4way_unroll的改进版,指令数更少,速度更快些。
     binAdd_base30_ALU_8way_unroll2 为 binAdd_base30_ALU_8way_unroll的改进版,指令数更少,速度更快些,但基本与binAdd_base30_ALU_4way_unroll2 持平。




Test function: binAdd_ALU(..) 100000 times...
Elapsed time: 1904.675 ms

Test function: binAdd_GMP_ALU_8way_unroll(..) 100000 times...
Elapsed time: 1357.660 ms

Test function: binAdd_Yaos_MMX_4way_unroll(..) 100000 times...
Elapsed time: 772.390 ms

Test function: binAdd_base30_ALU(..) 100000 times...
Elapsed time: 1203.820 ms

Test function: binAdd_base30_ALU_4way_unroll(..) 100000 times...
Elapsed time: 1225.580 ms

Test function: binAdd_base30_ALU_4way_unroll2(..) 100000 times...
Elapsed time: 1190.035 ms

Test function: binAdd_base30_ALU_8way_unroll(..) 100000 times...
Elapsed time: 1213.810 ms

Test function: binAdd_base30_ALU_8way_unroll2(..) 100000 times...
Elapsed time: 1189.320 ms

Test function: binAdd_Base30_MMX_4way_unroll(..) 100000 times...
Elapsed time: 903.745 ms

再给出测试代码:
//基为2^30的版本另一个版本,采用4路循环展开
  1. _declspec(naked)
  2. DWORD binAdd_base30_ALU_4way_unroll2(DWORD *dst,
  3. const DWORD *src1,
  4. const DWORD *src2,
  5. int   size)
  6. {
  7. #undef  BASE30_MASK
  8. #define BASE30_MASK 0x3fffffff
  9. _asm
  10. {
  11. push edi
  12. push esi
  13. push ebx
  14. sub  esp,4

  15. mov ebx,[esp+16+PARAM_DST]
  16. mov esi,[esp+16+PARAM_SRC1]
  17. mov edi,[esp+16+PARAM_SRC2]
  18. mov edx,[esp+16+PARAM_SIZE]

  19. lea eax,[esi+edx*4]
  20. mov [esp],eax

  21. xor ecx,ecx //clear carry
  22. and edx,3
  23. jz  a10

  24. loop00:
  25. add ecx,[esi]
  26. add ecx,[edi]
  27. mov ecx,eax
  28. and eax,BASE30_MASK
  29. shr ecx,30
  30. mov [ebx],eax
  31. lea esi,[esi+4]
  32. lea edi,[edi+4]
  33. lea ebx,[ebx+4]
  34. dec edx
  35. jnz loop00
  36. a10:
  37. jmp cmp00

  38. ALIGN 8
  39. loop01:
  40. add ecx, [esi]
  41. add ecx, [edi]
  42. mov eax, ecx
  43. and eax,BASE30_MASK
  44. shr ecx,30
  45. mov [ebx],eax

  46. add ecx, [esi+4]
  47. add ecx, [edi+4]
  48. mov edx, ecx
  49. and edx,BASE30_MASK
  50. shr ecx,30
  51. mov [ebx+4],edx


  52. add ecx, [esi+8]
  53. add ecx, [edi+8]
  54. mov eax, ecx
  55. and eax,BASE30_MASK
  56. shr ecx,30
  57. mov [ebx+8],eax


  58. add ecx, [esi+12]
  59. add ecx, [edi+12]
  60. mov edx, ecx
  61. and edx,BASE30_MASK
  62. shr ecx,30
  63. mov [ebx+12],edx


  64. lea esi,[esi+16]
  65. lea edi,[edi+16]
  66. lea ebx,[ebx+16]
  67. cmp00:
  68. cmp esi,[esp]
  69. jb loop01

  70. thisExit:
  71. add esp,4

  72. pop ebx
  73. pop esi
  74. pop edi
  75. mov eax,0
  76. adc eax,0
  77. ret
  78. }
  79. }
复制代码
//基为2^30的版本另一个版本,指令数更少,采用8路循环展开
  1. _declspec(naked)
  2. DWORD binAdd_base30_ALU_8way_unroll2(DWORD *dst,
  3. const DWORD *src1,
  4. const DWORD *src2,
  5. int   size)
  6. {
  7. #undef  BASE30_MASK
  8. #define BASE30_MASK 0x3fffffff
  9. _asm
  10. {
  11. push edi
  12. push esi
  13. push ebx
  14. sub  esp,4

  15. mov ebx,[esp+16+PARAM_DST]
  16. mov esi,[esp+16+PARAM_SRC1]
  17. mov edi,[esp+16+PARAM_SRC2]
  18. mov edx,[esp+16+PARAM_SIZE]

  19. lea eax,[esi+edx*4]
  20. mov [esp],eax

  21. xor ecx,ecx
  22. and edx,7
  23. jz  a10

  24. loop00:
  25. add ecx,[esi]
  26. add ecx,[edi]
  27. mov eax,ecx
  28. and eax,BASE30_MASK
  29. shr ecx,30
  30. mov [ebx],eax
  31. lea esi,[esi+4]
  32. lea edi,[edi+4]
  33. lea ebx,[ebx+4]
  34. dec edx
  35. jnz loop00
  36. a10:
  37. jmp cmp00

  38. ALIGN 8
  39. loop01:
  40. add ecx,[esi]
  41. add ecx,[edi]
  42. mov eax,ecx
  43. and eax,BASE30_MASK
  44. shr ecx,30
  45. mov [ebx],eax


  46. add ecx,[esi+4]
  47. add ecx,[edi+4]
  48. mov edx,ecx
  49. and edx,BASE30_MASK
  50. shr ecx,30
  51. mov [ebx+4],edx


  52. add ecx,[esi+8]
  53. add ecx,[edi+8]
  54. mov eax,ecx
  55. and eax,BASE30_MASK
  56. shr ecx,30
  57. mov [ebx+8],eax


  58. add ecx,[esi+12]
  59. add ecx,[edi+12]
  60. mov edx,ecx
  61. and edx,BASE30_MASK
  62. shr ecx,30
  63. mov [ebx+12],edx


  64. add ecx,[esi+16]
  65. add ecx,[edi+16]
  66. mov eax,ecx
  67. and eax,BASE30_MASK
  68. shr ecx,30
  69. mov [ebx+16],eax


  70. add ecx,[esi+20]
  71. add ecx,[edi+20]
  72. mov edx,ecx
  73. and edx,BASE30_MASK
  74. shr ecx,30
  75. mov [ebx+20],edx


  76. add ecx,[esi+24]
  77. add ecx,[edi+24]
  78. mov eax,ecx
  79. and eax,BASE30_MASK
  80. shr ecx,30
  81. mov [ebx+24],eax


  82. add ecx,[esi+28]
  83. add ecx,[edi+28]
  84. mov edx,ecx
  85. and edx,BASE30_MASK
  86. shr ecx,30
  87. mov [ebx+28],edx


  88. lea esi,[esi+32]
  89. lea edi,[edi+32]
  90. lea ebx,[ebx+32]
  91. cmp00:
  92. cmp esi,[esp]
  93. jb loop01

  94. thisExit:
  95. add esp,4
  96. pop ebx
  97. pop esi
  98. pop edi
  99. mov eax,0
  100. adc eax,0
  101. ret
  102. }
  103. }
复制代码


毋因群疑而阻独见  毋任己意而废人言
毋私小惠而伤大体  毋借公论以快私情
 楼主| 发表于 2008-5-8 16:56:16 | 显示全部楼层


刚把加法的SSE2加4路展开给通用化了
并顺便实现了8路展开

1024双字加,重复100万次
4路SSE2加: 2547.029
8路SSE2加: 2456.956

8路的仅提高3.5%时间
所以4路的为最佳形式了

附四路八路通用加法代码

DWORD longAdd4p(DWORD result[], DWORD left[], DWORD right[], DWORD len)
{
    __asm
  {
  mov esi, [left]
  mov edi, [right]
  mov ebx, [result]
  mov ecx, [len]
  mov edx, ecx
  pxor mm0, mm0
  shr ecx, 2
  je  loop20
  loop1:
  movd mm1, [esi]
  movd mm2, [edi]
  paddq mm1,  mm2
  movd mm3, [esi+4]
  movd mm4, [edi+4]
  paddq mm3, mm4
  movd mm5, [esi+8]
  movd mm6, [edi+8]
  paddq mm5, mm6
  movd mm7, [esi+12]
  movd mm3, [edi+12]
  paddq mm7, mm3
  paddq mm0, mm1
  movd [ebx], mm0
  psrlq mm0, 32
  paddq mm0, mm3
  movd [ebx+4], mm0
  psrlq mm0, 32
  paddq mm0, mm5
  movd [ebx+8], mm0
  psrlq mm0, 32
  paddq mm0, mm7
  movd [ebx+12], mm0
  psrlq mm0, 32
  add esi, 16
  add edi, 16
  add ebx, 16
  sub ecx, 1
  jne loop1
loop20:
  mov ecx, edx
  and ecx, 3
  jz exit1
loop2:
  movd mm1, [esi]
  movd mm2, [edi]
  paddq mm0, mm1
  paddq mm0, mm2
  movd [ebx], mm0
  psrlq mm0, 32
  add esi, 4
  add edi, 4
  add ebx, 4
  sub ecx, 1
  jne loop2
exit1:
  movd eax, mm0
  emms
  }
}

DWORD longAdd8p(DWORD result[], DWORD left[], DWORD right[], DWORD len)
{
    __asm
  {
  mov esi, [left]
  mov edi, [right]
  mov ebx, [result]
  mov ecx, [len]
  mov edx, ecx
  pxor mm0, mm0
  shr ecx, 3
  je  loop20
  loop1:
  movd mm1, [esi]
  movd mm2, [edi]
  paddq mm1,  mm2
  movd mm3, [esi+4]
  movd mm4, [edi+4]
  paddq mm3, mm4
  movd mm5, [esi+8]
  movd mm6, [edi+8]
  paddq mm5, mm6
  movd mm7, [esi+12]
  movd mm3, [edi+12]
  paddq mm7, mm3
  movd mm2, [esi+16]
  movd mm4, [edi+16]
  paddq mm2, mm4
  paddq mm0, mm1
  movd [ebx], mm0
  psrlq mm0, 32
  movd mm6, [esi+20]
  movd mm1, [edi+20]
  paddq mm6, mm1
  paddq mm0, mm3
  movd [ebx+4], mm0
  psrlq mm0, 32
  paddq mm0, mm5
  movd [ebx+8], mm0
  psrlq mm0, 32
  movd mm4, [esi+24]
  movd mm5, [edi+24]
  paddq mm4, mm5
  paddq mm0, mm7
  movd [ebx+12], mm0
  psrlq mm0, 32
  movd mm1, [esi+28]
  movd mm3, [edi+28]
  paddq mm1, mm3
  paddq mm0, mm2
  movd [ebx+16], mm0
  psrlq mm0, 32
  paddq mm0, mm6
  movd [ebx+20], mm0
  psrlq mm0, 32
  paddq mm0, mm4
  movd [ebx+24], mm0
  psrlq mm0, 32
  paddq mm0, mm1
  movd [ebx+28], mm0
  psrlq mm0, 32
  add esi, 32
  add edi, 32
  add ebx, 32
  sub ecx, 1
  jne loop1
loop20:
  mov ecx, edx
  and ecx, 7
  jz exit1
loop2:
  movd mm1, [esi]
  movd mm2, [edi]
  paddq mm0, mm1
  paddq mm0, mm2
  movd [ebx], mm0
  psrlq mm0, 32
  add esi, 4
  add edi, 4
  add ebx, 4
  sub ecx, 1
  jne loop2
exit1:
  movd eax, mm0
  emms
  }
}
毋因群疑而阻独见  毋任己意而废人言
毋私小惠而伤大体  毋借公论以快私情
发表于 2008-5-8 17:08:55 | 显示全部楼层
原帖由 liangbch 于 2008-4-8 18:05 发表
对于编写高性能的程序,常常需要权衡程序效率和复杂度之间的平衡。1路->4路,性能提高明显, 4路 -〉8路 则性能提高的就很有限了。从GMP的源代码可以看出,他使用的循环展开一般为4-6路。个人觉得4路循环展开应该是一 ...


看来我在16#的断言没有错。
毋因群疑而阻独见  毋任己意而废人言
毋私小惠而伤大体  毋借公论以快私情
 楼主| 发表于 2008-5-8 17:30:44 | 显示全部楼层
如果实现求圆周率程序
8路展开也许还有用处的
毋因群疑而阻独见  毋任己意而废人言
毋私小惠而伤大体  毋借公论以快私情
发表于 2008-5-9 10:45:16 | 显示全部楼层
8路循环展开也不是没有用武之地,对于循环体内代码很少的情况下,8路循环展开还是有优势的,如内存清0,内存复制。memcpy 源代码就使用了8路循环展开。
毋因群疑而阻独见  毋任己意而废人言
毋私小惠而伤大体  毋借公论以快私情
 楼主| 发表于 2008-5-9 10:55:52 | 显示全部楼层


那可以考虑和16路展开比较
毋因群疑而阻独见  毋任己意而废人言
毋私小惠而伤大体  毋借公论以快私情
发表于 2008-5-9 11:50:49 | 显示全部楼层
可以预计16路循环展开对性能的提升极为有限。

哈哈,我还是想说一句,我的原则是有所为,有所不为,为了1%的性能提高,而花费%20的时间和精力,我一般不会去做。
毋因群疑而阻独见  毋任己意而废人言
毋私小惠而伤大体  毋借公论以快私情
 楼主| 发表于 2008-5-9 14:36:58 | 显示全部楼层
我做4路加法消耗20-30分钟
4路改8路仅修改了10分钟

所以如果你改16路不划算
证明你代码存在难修改的问题
结构化不好

毋因群疑而阻独见  毋任己意而废人言
毋私小惠而伤大体  毋借公论以快私情
发表于 2008-5-9 15:48:20 | 显示全部楼层
哈哈,上面只是一个比方,我也不知道改一个函数需要耗费多少成本。
  不过8路改16路应该很简单,逻辑上应该没有多大变化,但相应的也需要写一堆测试代码。
8路改为16路,提速效果应该不大,我不想尝试了。我下一步想要做的是,实现$2^30$的basecase乘法。
毋因群疑而阻独见  毋任己意而废人言
毋私小惠而伤大体  毋借公论以快私情
您需要登录后才可以回帖 登录 | 欢迎注册

本版积分规则

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

GMT+8, 2024-5-3 12:59 , Processed in 0.042939 second(s), 15 queries .

Powered by Discuz! X3.5

© 2001-2024 Discuz! Team.

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