找回密码
 欢迎注册
楼主: liangbch

[原创] 求一个数组中元素的最大值和最小值

[复制链接]
发表于 2010-12-22 09:28:02 | 显示全部楼层
读会同时读到cache,写直接写内存的 难道是读比写慢的原因
毋因群疑而阻独见  毋任己意而废人言
毋私小惠而伤大体  毋借公论以快私情
发表于 2010-12-22 11:58:35 | 显示全部楼层
写也是到Cache,不一定到内存,所以像下面的代码: 初时x=0,y=0 线程1: x=1; y=1; ... 线程2: if(y==1&&x==0){ ... } 线程2这个if中的内容是有可能被执行到的.
毋因群疑而阻独见  毋任己意而废人言
毋私小惠而伤大体  毋借公论以快私情
发表于 2010-12-27 18:21:29 | 显示全部楼层
8# liangbch 奇怪,你说带宽为6.4GB/s ,你的read为6826MB/s >6.4GB/s ,难道还没有真正达到内存瓶颈?
毋因群疑而阻独见  毋任己意而废人言
毋私小惠而伤大体  毋借公论以快私情
 楼主| 发表于 2010-12-28 13:13:55 | 显示全部楼层
我认为, 测试软件得到的结果并不是100%精确,他总是存在误差,所有9#的测试结果大于理论值。 我家那台老电脑是DDR400,但是实测内存带宽明显小于理论值。 11#的内存也是DDR400, 理论带宽是3.2GB/s,但11#给出的内存读性能却比理论值高出好多,竟达到4.1GB/s
毋因群疑而阻独见  毋任己意而废人言
毋私小惠而伤大体  毋借公论以快私情
发表于 2010-12-28 13:56:11 | 显示全部楼层
24# liangbch 这样想就好了,呵呵,那个getMaxMin_SSE4 已在我这机子没啥潜力了,直接抱走。。
毋因群疑而阻独见  毋任己意而废人言
毋私小惠而伤大体  毋借公论以快私情
 楼主| 发表于 2011-1-1 23:16:48 | 显示全部楼层
1# liangbch  采用32M的测试样本,运行在Intel I5-750 4核CPU,同时测试这6个函数。每个函数均取5次测试结果的最小值,结果如下: getMaxMin1: 0.064071 seconds getMaxMin_asm: 0.065478 seconds getMaxMin_SSE4: 0.016766 seconds getMaxMin_MT2_v1: 0.027156 seconds getMaxMin_MT4_v1: 0.014782 seconds getMaxMin_MT2_v2: 0.014681 seconds getMaxMin_MT4_v4: 0.014684 seconds
毋因群疑而阻独见  毋任己意而废人言
毋私小惠而伤大体  毋借公论以快私情
 楼主| 发表于 2011-1-2 21:53:24 | 显示全部楼层
到目前为止,我编写了12个版本的求一个无符号整数数组的最大值和最小值的函数,函数名称和运行速度见下。 测试环境:i5-750四核CPU, DDR3-1333,2G内存, 测试的数据为32M个无符号整数,给出的时间值为5次测试的最小值。 getMaxMin1: 0.060888 秒 getMaxMin1_asm: 0.065483 秒 getMaxMin1_SSE4: 0.016788 秒 getMaxMin2: 0.128427 秒 getMaxMin2_asm1: 0.040059 秒 getMaxMin2_asm2: 0.040233 秒 getMaxMin_MT2_v1: 0.027078 秒 getMaxMin_MT4_v1: 0.014741 秒 getMaxMin_MT2_v2: 0.014625 秒 getMaxMin_MT4_v2: 0.014714 秒 getMaxMin_MT2_v3: 0.020532 秒 getMaxMin_MT4_v3: 0.014684 秒 函数getMaxMin1是一个最普通的版本,使用C语言书写,每个数需作2次比较,使用随机数据,CPU的分支预测部件预测成功的概率非常高。故速度很快。 函数getMaxMin1_asm是一个使用汇编语言书写的版本,使用 cmovb 指令消除分支,使得程序的性能与分支预测无关,速度得到保证,相比能够准确做分支预测的版本getMaxMin1,速度稍慢。测试结果表明,如果分支预测的正确率很高,消除分支并不能提高速度。 函数getMaxMin1_SSE4是一个使用汇编语言书写的版本,不同getMaxMin1_asm,这个函数使用SSE4 指令PMINUD和PMAXUD,一次可求4个DWORD的最大或者最小值,内存读则使用movdqa指令,一次读取4个DWORD,这个版本充分展示了SSE4指令的威力,速度竟达 getMaxMin1_asm 的四倍,几乎吃掉了整个内存带宽。 函数 getMaxMin2: 也是用C语言的书写的版本,每2数需要3次比较,比较次数较getMaxMin1更少,但是每2个数的3次比较中,第一次比较时是一个数据依赖的分支,CPU分支预测的准确率大约为50%,分支预测失败的惩罚大大降低了速度,速度仅为getMaxMin1的50%左右。 函数getMaxMin2_asm1:这个函数是 getMaxMin2的汇编版本,使用cmovb 指令消除了每2个数的3次比较,消除分子的效果非常明显,速度竟提高到300%。 函数getMaxMin2_asm2:这个函数也是 getMaxMin2的汇编版本,在每个循环步中,比getMaxMin2_asm1多了1条指令,但减少了内存访问,内存访问的减少并没有完全抵消一条mov指令的开销,速度和getMaxMin2_asm1大体相当,但多次测试的结果显示比getMaxMin2_asm1略慢。 后6个函数是2线程和4线程的版本。 getMaxMin_MT2_v1是getMaxMin1的2线程版本,速度提高到单线程版本的200%以上,在误差范围内,速度为单线程版本的2倍,和理论吻合。 getMaxMin_MT4_v1是getMaxMin1的4线程版本,速度提高到单线程版本的400%以上,在误差范围内,速度为单线程版本的4倍左右,和理论吻合。 getMaxMin_MT2_v2是getMaxMin1_SSE4的2线程版本,速度仅仅提高13%,一个线程的getMaxMin1_SSE4函数几乎吃到了整个内存带宽,故getMaxMin_MT2_v2性能的提高受限于内存带宽,并没有达到单线程版本的200%。 getMaxMin_MT4_v2是getMaxMin1_SSE4的4线程版本,速度和双线程版本getMaxMin_MT2_v2相同。性能的提高受限于内存带宽,并没有达到单线程版本的400%。 getMaxMin_MT2_v3是getMaxMin2_asm1的2线程版本,速度为单线程版本的195%,在误差范围内,速度为单线程版本的2倍,和理论吻合。 getMaxMin_MT4_v3是getMaxMin2_asm1的4线程版本,性能的提高受限于内存带宽,并没有达到单线程版本的400%。 以下4个多线程版本函数的性能几乎完全相同,而且实际性能都小于理论值,说明 多线程函数的执行单元虽然是独立的,但他们共享相同的物理内存,多线程并行的性能受限于内存的总带宽,当总的内存存取达到物理内存带宽的限制,增加线程数并不能提高实际性能。 getMaxMin_MT4_v1: 0.014741 秒 getMaxMin_MT2_v2: 0.014625 秒 getMaxMin_MT4_v2: 0.014714 秒 getMaxMin_MT4_v3: 0.014684 秒

评分

参与人数 1威望 +3 贡献 +3 鲜花 +3 收起 理由
gxqcn + 3 + 3 + 3 难得的一手数据。

查看全部评分

毋因群疑而阻独见  毋任己意而废人言
毋私小惠而伤大体  毋借公论以快私情
 楼主| 发表于 2011-1-2 22:06:50 | 显示全部楼层
给出6个单线程函数的源代码
  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <assert.h>
  4. #include "defs.h"
  5. #include "findMaxMin.h"
  6. void getMaxMin1(DWORD *pData,int len,DWORD *min,DWORD *max)
  7. {
  8. int i;
  9. *min=~0;
  10. *max=0;
  11. for (i=0; i<len; i++)
  12. {
  13. if ( pData[ i ] > *max)
  14. *max=pData[ i ];
  15. if (pData[ i ]< *min)
  16. *min=pData[ i ];
  17. }
  18. }
  19. void getMaxMin1_asm(DWORD *pData,int len,DWORD *min,DWORD *max)
  20. {
  21. DWORD _min, _max;
  22. __asm
  23. {
  24. mov esi, pData
  25. mov ecx, len
  26. lea edi, [esi+ecx*4]
  27. mov eax, 0xffffffff //min
  28. mov edx, 0 //max
  29. loop_start:
  30. cmp esi, edi
  31. jge next10
  32. cmp [esi], eax
  33. cmovb eax,[esi]
  34. cmp [esi], edx
  35. cmova edx,[esi]
  36. add esi, 4
  37. jmp loop_start
  38. next10:
  39. mov _min, eax
  40. mov _max, edx
  41. }
  42. *min=_min;
  43. *max=_max;
  44. }
  45. void getMaxMin2(DWORD *pData,int len,DWORD *min,DWORD *max)
  46. {
  47. int i;
  48. if (len % 2 ==0 )
  49. {
  50. if ( pData[0] > pData[1] )
  51. {
  52. *max= pData[0];
  53. *min= pData[1];
  54. }
  55. else
  56. {
  57. *max= pData[1];
  58. *min= pData[0];
  59. }
  60. i=2;
  61. }
  62. else
  63. {
  64. *max= *min= pData[0];
  65. i=1;
  66. }
  67. for (;i<len;i+=2)
  68. {
  69. if ( pData[ i ] > pData[ i+1] )
  70. {
  71. if ( pData[ i ]> *max)
  72. *max=pData[ i ];
  73. if ( pData[ i+1]< *min)
  74. *min=pData[ i+1];
  75. }
  76. else
  77. {
  78. if ( pData[ i ] < *min)
  79. *min=pData[ i ];
  80. if ( pData[ i+1]> *max)
  81. *max=pData[ i+1];
  82. }
  83. }
  84. }
  85. void getMaxMin2_asm1(DWORD *pData,int len,DWORD *min,DWORD *max)
  86. {
  87. DWORD _min, _max;
  88. __asm
  89. {
  90. mov esi, pData
  91. mov ecx, len
  92. lea edi, [esi+ecx*4]
  93. and ecx,1
  94. jz next10
  95. //next00: next % 2 ==1
  96. mov eax, [esi]
  97. mov edx, [esi]
  98. add esi,4
  99. jmp next20
  100. next10: // len % 2 ==0
  101. mov eax,[esi] // eax: min
  102. mov edx,[esi+4] // edx, max
  103. cmp eax,edx
  104. jb next15
  105. xchg eax,edx
  106. next15:
  107. add esi,8
  108. jmp next20
  109. loop_start:
  110. mov ebx, [esi] //ebx=min(pData [i] , pData [ i+1]
  111. mov ecx, [esi+4] //ebx=max(pData [i] , pData [ i+1]
  112. cmp ebx, ecx
  113. cmova ebx, [esi+4]
  114. cmova ecx, [esi]
  115. cmp eax, ebx
  116. cmova eax, ebx
  117. cmp edx, ecx
  118. cmovb edx, ecx
  119. add esi, 8
  120. next20:
  121. cmp esi, edi
  122. jb loop_start
  123. mov _min, eax
  124. mov _max, edx
  125. }
  126. *min=_min;
  127. *max=_max;
  128. }
  129. _declspec(naked)
  130. void getMaxMin2_asm2(DWORD *pData,int len,DWORD *min,DWORD *max)
  131. {
  132. #define PAR_PDATA 4
  133. #define PAR_LEN 8
  134. #define PAR_MIN 12
  135. #define PAR_MAX 16
  136. #define REG_MIN esi
  137. #define REG_MAX edi
  138. #define REG_CUR_TMP ecx
  139. #define REG_CUR_MIN eax
  140. #define REG_CUR_MAX edx
  141. #define REG_PDATA ebx // pData
  142. #define REG_PEND ebp // pData+len
  143. __asm
  144. {
  145. push ebx
  146. push ebp
  147. push esi
  148. push edi
  149. mov REG_PDATA, dword ptr[esp+16+PAR_PDATA]
  150. mov ecx,dword ptr[esp+16+PAR_LEN]
  151. test ecx,1
  152. jz next10
  153. //next00: next % 2 ==1
  154. lea REG_PEND, [REG_PDATA+ecx*4]
  155. mov REG_MIN, [REG_PDATA]
  156. mov REG_MAX, [REG_PDATA]
  157. add REG_PDATA,4
  158. jmp next20
  159. next10: // len % 2 ==0
  160. lea REG_PEND, [REG_PDATA+ecx*4]
  161. mov REG_MIN,[REG_PDATA]
  162. mov REG_MAX,[REG_PDATA+4]
  163. cmp REG_MIN,REG_MAX
  164. jb next15
  165. xchg REG_MIN,REG_MAX
  166. next15:
  167. add REG_PDATA,8
  168. jmp next20
  169. loop_start:
  170. mov REG_CUR_MIN, [REG_PDATA] //ebx=min(pData [i] , pData [ i+1]
  171. mov REG_CUR_MAX, [REG_PDATA+4] //ebx=max(pData [i] , pData [ i+1]
  172. cmp REG_CUR_MIN, REG_CUR_MAX
  173. mov REG_CUR_TMP, REG_CUR_MIN
  174. cmova REG_CUR_MIN, REG_CUR_MAX
  175. cmova REG_CUR_MAX, REG_CUR_TMP
  176. cmp REG_MIN, REG_CUR_MIN
  177. cmova REG_MIN, REG_CUR_MIN
  178. cmp REG_MAX, REG_CUR_MAX
  179. cmovb REG_MAX, REG_CUR_MAX
  180. add REG_PDATA, 8
  181. next20:
  182. cmp REG_PDATA, REG_PEND
  183. jb loop_start
  184. mov eax, [esp+16+PAR_MIN]
  185. mov edx, [esp+16+PAR_MAX]
  186. mov [eax], REG_MIN
  187. mov [edx], REG_MAX
  188. pop edi
  189. pop esi
  190. pop ebp
  191. pop ebx
  192. ret
  193. }
  194. }
  195. _declspec(naked)
  196. void getMaxMin1_SSE4(DWORD *pData,int len,DWORD *min,DWORD *max)
  197. // 使用SSE4.1 指令 计算数组的最大最小值
  198. // 寄存器的使用
  199. // eax: 存放最小值
  200. // edx: 存放最大值
  201. // xmm0: 当前取得的4个整数
  202. // xmm1: 存放最小值
  203. // xmm2: 存放最大值
  204. /* PMINUD and PMAXUD are SSE4.1 instruciton
  205. The usage for PMINUD
  206. Compares packed unsigned dword integers in the destination operand (first operand)
  207. and the source operand (second operand), and returns the minimum for each packed
  208. value in the destination operand.
  209. Operation
  210. IF (DEST[31:0] < SRC[31:0])
  211. THEN DEST[31:0] &#1048773; DEST[31:0];
  212. ELSE DEST[31:0] &#1048773; SRC[31:0]; FI;
  213. IF (DEST[63:32] < SRC[63:32])
  214. THEN DEST[63:32] &#1048773; DEST[63:32];
  215. ELSE DEST[63:32] &#1048773; SRC[63:32]; FI;
  216. IF (DEST[95:64] < SRC[95:64])
  217. THEN DEST[95:64] &#1048773; DEST[95:64];
  218. ELSE DEST[95:64] &#1048773; SRC[95:64]; FI;
  219. IF (DEST[127:96] < SRC[127:96])
  220. THEN DEST[127:96] &#1048773; DEST[127:96];
  221. ELSE DEST[127:96] &#1048773; SRC[127:96]; FI;
  222. */
  223. #define MIN_128REG xmm1
  224. #define MAX_128REG xmm2
  225. #define MIN_32REG eax
  226. #define MAX_32REG edx
  227. #define PT_REG esi
  228. #define END_REG edi
  229. #define PAR_PDATA 4
  230. #define PAR_LEN 8
  231. #define PAR_MIN 12
  232. #define PAR_MAX 16
  233. {
  234. __asm
  235. {
  236. push esi
  237. push edi
  238. mov MIN_32REG, 0xffffffff // min=0xffffffff
  239. xor MAX_32REG, MAX_32REG // max=0
  240. //phase1:
  241. mov ecx, dword ptr[esp+8+PAR_LEN] // len
  242. mov PT_REG, dword ptr[esp+8+PAR_PDATA] // pData
  243. lea END_REG, [PT_REG+ecx*4] // edi=pData + len
  244. jmp cmp10
  245. loop1_start:
  246. cmp [PT_REG], MIN_32REG
  247. cmovb MIN_32REG,[PT_REG]
  248. cmp [PT_REG], MAX_32REG
  249. cmova MAX_32REG,[PT_REG]
  250. add PT_REG, 4
  251. cmp10:
  252. test PT_REG, 0x0f
  253. jz phase2
  254. cmp PT_REG, END_REG
  255. jb loop1_start
  256. phase2:
  257. mov ecx, dword ptr[esp+8+PAR_LEN] // len
  258. mov END_REG, dword ptr[esp+8+PAR_PDATA] // pData
  259. lea END_REG, [END_REG+ecx*4] // edi=pData + len
  260. and END_REG, 0xfffffff0 // clear bit0-bit3 and make edi % 16==0
  261. movd MIN_128REG, MIN_32REG
  262. pshufd MIN_128REG, MIN_128REG, 00000000b // xmm1 = R0:R0:R0:R0
  263. movd MAX_128REG, MAX_32REG
  264. pshufd MAX_128REG, MAX_128REG, 00000000b // xmm2 = R0:R0:R0:R0
  265. jmp cmp20
  266. loop2_start:
  267. movdqa xmm0, xmmword ptr[PT_REG]
  268. PMINUD MIN_128REG, xmm0
  269. PMAXUD MAX_128REG, xmm0
  270. add PT_REG,16
  271. cmp20:
  272. cmp PT_REG, END_REG
  273. jb loop2_start
  274. phase3:
  275. movd ecx, MIN_128REG
  276. cmp ecx, MIN_32REG
  277. cmovb MIN_32REG,ecx //MIN_32REG=min(min_32REG,bit0-31(MIN_128REG))
  278. psrldq MIN_128REG, 4 //MIN_128REG >>=4
  279. movd ecx, MIN_128REG
  280. cmp ecx, MIN_32REG
  281. cmovb MIN_32REG,ecx //MIN_32REG=min(min_32REG,bit32-63(MIN_128REG))
  282. psrldq MIN_128REG, 4 //MIN_128REG >>=4
  283. movd ecx, MIN_128REG
  284. cmp ecx, MIN_32REG
  285. cmovb MIN_32REG,ecx //MIN_32REG=min(min_32REG,bit64-95(MIN_128REG))
  286. psrldq MIN_128REG, 4 //MIN_128REG >>=4
  287. movd ecx, MIN_128REG
  288. cmp ecx, MIN_32REG
  289. cmovb MIN_32REG,ecx //MIN_32REG=min(min_32REG,bit96-127(MIN_128REG) )
  290. //-----------------------------------------------
  291. movd ecx, MAX_128REG
  292. cmp ecx, MAX_32REG
  293. cmova MAX_32REG,ecx //MAX_32REG=max(max_32REG,bit0-31(MAX_128REG))
  294. psrldq MAX_128REG, 4 //MAX_128REG >>=4
  295. movd ecx, MAX_128REG
  296. cmp ecx, MAX_32REG
  297. cmova MAX_32REG,ecx //MAX_32REG=max(max_32REG,bit32-63(MAX_128REG))
  298. psrldq MAX_128REG, 4 //MAX_128REG >>=4
  299. movd ecx, MAX_128REG
  300. cmp ecx, MAX_32REG
  301. cmova MAX_32REG,ecx //MAX_32REG=max(max_32REG,bit64-95(MAX_128REG))
  302. psrldq MAX_128REG, 4 //MAX_128REG >>=4
  303. movd ecx, MAX_128REG
  304. cmp ecx, MAX_32REG
  305. cmova MAX_32REG,ecx //MIN_32REG=max(max_32REG,bit96-127(MIN_128REG))
  306. mov END_REG, dword ptr[esp+8+PAR_PDATA] // pData
  307. mov ecx, dword ptr[esp+8+PAR_LEN] // len
  308. lea END_REG, [END_REG+ecx*4] // edi=pData + len
  309. jmp cmp30
  310. loop3_start:
  311. cmp [PT_REG], MIN_32REG
  312. cmovb MIN_32REG,[PT_REG]
  313. cmp [PT_REG], MAX_32REG
  314. cmova MAX_32REG,[PT_REG]
  315. add PT_REG, 4
  316. cmp30:
  317. cmp PT_REG, END_REG
  318. jb loop3_start
  319. RET_VALUE:
  320. mov ecx, dword ptr[esp+8+PAR_MIN] //*min
  321. mov dword ptr [ecx],MIN_32REG
  322. mov ecx, dword ptr[esp+8+PAR_MAX] //*max
  323. mov dword ptr [ecx],MAX_32REG
  324. emms
  325. pop edi
  326. pop esi
  327. ret
  328. }
  329. }
复制代码
毋因群疑而阻独见  毋任己意而废人言
毋私小惠而伤大体  毋借公论以快私情
 楼主| 发表于 2011-1-2 22:14:00 | 显示全部楼层
本楼给出 包括VC2008工程文件的所有源代码。

findMaxMin.zip

14.92 KB, 下载次数: 3, 下载积分: 金币 -1 枚, 经验 1 点, 下载 1 次

售价: 5 枚金币  [记录]

毋因群疑而阻独见  毋任己意而废人言
毋私小惠而伤大体  毋借公论以快私情
发表于 2011-1-4 08:19:38 | 显示全部楼层
非常赞赏这种讨论方式,有代码、有数据、有分析结论,相当完整,非常方便大家参考。
毋因群疑而阻独见  毋任己意而废人言
毋私小惠而伤大体  毋借公论以快私情
您需要登录后才可以回帖 登录 | 欢迎注册

本版积分规则

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

GMT+8, 2024-11-22 04:23 , Processed in 0.029563 second(s), 19 queries .

Powered by Discuz! X3.5

© 2001-2024 Discuz! Team.

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