找回密码
 欢迎注册
楼主: 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.    
  49.     if (len % 2 ==0 )
  50.     {
  51.         if ( pData[0] > pData[1] )
  52.         {
  53.             *max= pData[0];
  54.             *min= pData[1];
  55.         }
  56.         else
  57.         {
  58.             *max= pData[1];
  59.             *min= pData[0];
  60.         }
  61.         i=2;
  62.     }
  63.     else
  64.     {
  65.         *max= *min= pData[0];
  66.         i=1;
  67.     }
  68.    
  69.     for (;i<len;i+=2)
  70.     {
  71.         if ( pData[ i ] > pData[ i+1] )
  72.         {
  73.             if ( pData[ i ]> *max)
  74.               *max=pData[ i ];
  75.             
  76.             if ( pData[ i+1]< *min)
  77.               *min=pData[ i+1];
  78.         }
  79.         else
  80.         {
  81.             if ( pData[ i ] < *min)
  82.               *min=pData[ i ];
  83.             
  84.             if ( pData[ i+1]> *max)
  85.               *max=pData[ i+1];
  86.         }
  87.     }
  88. }

  89. void getMaxMin2_asm1(DWORD *pData,int len,DWORD *min,DWORD *max)
  90. {
  91.     DWORD _min, _max;
  92.     __asm
  93.     {
  94.         mov   esi, pData
  95.         mov   ecx, len
  96.         lea   edi, [esi+ecx*4]

  97.         and   ecx,1
  98.         jz    next10

  99. //next00:  next % 2 ==1         
  100.         mov   eax,  [esi]
  101.         mov   edx,  [esi]
  102.         add   esi,4
  103.         jmp   next20

  104. next10:  // len % 2 ==0
  105.         mov   eax,[esi]         // eax: min
  106.         mov   edx,[esi+4]     // edx, max
  107.         cmp   eax,edx
  108.         jb    next15
  109.         xchg  eax,edx
  110. next15:
  111.         add   esi,8
  112.         jmp   next20

  113. loop_start:
  114.         mov   ebx, [esi]                //ebx=min(pData [i] , pData [ i+1]
  115.         mov   ecx, [esi+4]   //ebx=max(pData [i] , pData [ i+1]
  116.         cmp   ebx,  ecx
  117.         cmova ebx, [esi+4]
  118.         cmova ecx, [esi]

  119.         cmp   eax, ebx
  120.         cmova eax, ebx

  121.         cmp   edx, ecx
  122.         cmovb edx, ecx

  123.         add   esi, 8
  124. next20:
  125.         cmp   esi, edi
  126.         jb    loop_start
  127.                
  128.         mov   _min, eax
  129.         mov   _max, edx
  130.     }
  131.     *min=_min;
  132.     *max=_max;
  133. }

  134. _declspec(naked)
  135. void getMaxMin2_asm2(DWORD *pData,int len,DWORD *min,DWORD *max)
  136. {

  137. #define PAR_PDATA  4
  138. #define PAR_LEN    8
  139. #define PAR_MIN    12
  140. #define PAR_MAX    16

  141. #define REG_MIN  esi
  142. #define REG_MAX  edi

  143. #define REG_CUR_TMP  ecx
  144. #define REG_CUR_MIN  eax
  145. #define REG_CUR_MAX  edx
  146. #define REG_PDATA    ebx        // pData
  147. #define REG_PEND     ebp    // pData+len

  148.     __asm
  149.     {
  150.         push   ebx
  151.         push   ebp
  152.         push   esi
  153.         push   edi

  154.         mov    REG_PDATA, dword ptr[esp+16+PAR_PDATA]
  155.         mov    ecx,dword ptr[esp+16+PAR_LEN]

  156.         test   ecx,1
  157.         jz     next10

  158. //next00:  next % 2 ==1         
  159.         lea   REG_PEND, [REG_PDATA+ecx*4]
  160.         mov   REG_MIN,  [REG_PDATA]
  161.         mov   REG_MAX,  [REG_PDATA]
  162.         add   REG_PDATA,4
  163.         jmp   next20

  164. next10:  // len % 2 ==0
  165.         lea   REG_PEND,  [REG_PDATA+ecx*4]
  166.         mov   REG_MIN,[REG_PDATA]                 
  167.         mov   REG_MAX,[REG_PDATA+4]   
  168.         cmp   REG_MIN,REG_MAX
  169.         jb    next15
  170.         xchg  REG_MIN,REG_MAX
  171. next15:   
  172.         add   REG_PDATA,8
  173.         jmp   next20
  174.          
  175. loop_start:
  176.         mov   REG_CUR_MIN, [REG_PDATA]          //ebx=min(pData [i] , pData [ i+1]
  177.         mov   REG_CUR_MAX, [REG_PDATA+4]   //ebx=max(pData [i] , pData [ i+1]
  178.         
  179.         cmp   REG_CUR_MIN, REG_CUR_MAX
  180.         mov   REG_CUR_TMP, REG_CUR_MIN
  181.          
  182.         cmova REG_CUR_MIN, REG_CUR_MAX
  183.         cmova REG_CUR_MAX, REG_CUR_TMP
  184.         
  185.         cmp   REG_MIN, REG_CUR_MIN
  186.         cmova REG_MIN, REG_CUR_MIN
  187.                
  188.         cmp   REG_MAX, REG_CUR_MAX
  189.         cmovb REG_MAX, REG_CUR_MAX
  190.                
  191.         add   REG_PDATA, 8
  192. next20:
  193.         cmp   REG_PDATA, REG_PEND
  194.         jb    loop_start
  195.                
  196.         mov   eax, [esp+16+PAR_MIN]
  197.         mov   edx, [esp+16+PAR_MAX]
  198.         mov   [eax], REG_MIN
  199.         mov   [edx], REG_MAX
  200.         
  201.         pop   edi
  202.         pop   esi
  203.         pop   ebp
  204.         pop   ebx
  205.         ret
  206.    }

  207. }

  208. _declspec(naked)
  209. void getMaxMin1_SSE4(DWORD *pData,int len,DWORD *min,DWORD *max)

  210. // 使用SSE4.1 指令 计算数组的最大最小值
  211. // 寄存器的使用
  212. // eax: 存放最小值
  213. // edx: 存放最大值
  214. // xmm0: 当前取得的4个整数
  215. // xmm1: 存放最小值
  216. // xmm2: 存放最大值

  217. /* PMINUD and PMAXUD are SSE4.1 instruciton
  218. The usage for PMINUD
  219. Compares packed unsigned dword integers in the destination operand (first operand)
  220. and the source operand (second operand), and returns the minimum for each packed
  221. value in the destination operand.

  222. Operation
  223. IF (DEST[31:0] < SRC[31:0])
  224. THEN DEST[31:0] &#1048773; DEST[31:0];
  225. ELSE DEST[31:0] &#1048773; SRC[31:0]; FI;

  226. IF (DEST[63:32] < SRC[63:32])
  227. THEN DEST[63:32] &#1048773; DEST[63:32];
  228. ELSE DEST[63:32] &#1048773; SRC[63:32]; FI;

  229. IF (DEST[95:64] < SRC[95:64])
  230. THEN DEST[95:64] &#1048773; DEST[95:64];
  231. ELSE DEST[95:64] &#1048773; SRC[95:64]; FI;

  232. IF (DEST[127:96] < SRC[127:96])
  233. THEN DEST[127:96] &#1048773; DEST[127:96];
  234. ELSE DEST[127:96] &#1048773; SRC[127:96]; FI;
  235. */

  236. #define MIN_128REG      xmm1
  237. #define MAX_128REG      xmm2
  238. #define MIN_32REG      eax
  239. #define MAX_32REG      edx
  240. #define PT_REG          esi
  241. #define END_REG          edi

  242. #define PAR_PDATA     4
  243. #define PAR_LEN       8
  244. #define PAR_MIN       12
  245. #define PAR_MAX       16

  246. {
  247.     __asm
  248.     {
  249.         push   esi
  250.         push   edi

  251.         mov    MIN_32REG,  0xffffffff              // min=0xffffffff
  252.         xor    MAX_32REG,  MAX_32REG               // max=0

  253. //phase1:
  254.         mov    ecx, dword ptr[esp+8+PAR_LEN]       // len
  255.         mov    PT_REG, dword ptr[esp+8+PAR_PDATA]  // pData
  256.         lea    END_REG, [PT_REG+ecx*4]             // edi=pData + len
  257.         jmp    cmp10

  258. loop1_start:
  259.         cmp    [PT_REG], MIN_32REG
  260.         cmovb  MIN_32REG,[PT_REG]

  261.         cmp    [PT_REG], MAX_32REG
  262.         cmova  MAX_32REG,[PT_REG]

  263.         add    PT_REG, 4
  264. cmp10:
  265.         test   PT_REG, 0x0f
  266.         jz     phase2

  267.         cmp    PT_REG, END_REG
  268.         jb     loop1_start

  269. phase2:
  270.         mov     ecx, dword ptr[esp+8+PAR_LEN]        // len
  271.         mov     END_REG, dword ptr[esp+8+PAR_PDATA]  // pData
  272.         lea     END_REG, [END_REG+ecx*4]             // edi=pData + len
  273.         and     END_REG, 0xfffffff0                  // clear bit0-bit3 and make edi % 16==0

  274.                 movd    MIN_128REG,    MIN_32REG
  275.         pshufd  MIN_128REG, MIN_128REG, 00000000b    // xmm1 = R0:R0:R0:R0
  276.         movd    MAX_128REG,    MAX_32REG
  277.         pshufd  MAX_128REG, MAX_128REG, 00000000b    // xmm2 = R0:R0:R0:R0
  278.         jmp            cmp20

  279. loop2_start:
  280.         movdqa  xmm0, xmmword ptr[PT_REG]
  281.         PMINUD  MIN_128REG, xmm0
  282.         PMAXUD  MAX_128REG, xmm0

  283.         add     PT_REG,16
  284. cmp20:
  285.         cmp     PT_REG, END_REG
  286.         jb      loop2_start


  287. phase3:
  288.         movd    ecx, MIN_128REG
  289.         cmp     ecx, MIN_32REG
  290.         cmovb   MIN_32REG,ecx            //MIN_32REG=min(min_32REG,bit0-31(MIN_128REG))

  291.         psrldq  MIN_128REG, 4            //MIN_128REG >>=4
  292.         movd    ecx, MIN_128REG
  293.         cmp     ecx, MIN_32REG
  294.         cmovb   MIN_32REG,ecx            //MIN_32REG=min(min_32REG,bit32-63(MIN_128REG))

  295.         psrldq  MIN_128REG, 4            //MIN_128REG >>=4
  296.         movd    ecx, MIN_128REG
  297.         cmp     ecx, MIN_32REG
  298.         cmovb   MIN_32REG,ecx            //MIN_32REG=min(min_32REG,bit64-95(MIN_128REG))

  299.         psrldq  MIN_128REG, 4            //MIN_128REG >>=4
  300.         movd    ecx, MIN_128REG
  301.         cmp     ecx, MIN_32REG
  302.         cmovb   MIN_32REG,ecx            //MIN_32REG=min(min_32REG,bit96-127(MIN_128REG)    )

  303.         //-----------------------------------------------
  304.         movd    ecx, MAX_128REG
  305.         cmp     ecx, MAX_32REG
  306.         cmova   MAX_32REG,ecx            //MAX_32REG=max(max_32REG,bit0-31(MAX_128REG))

  307.         psrldq  MAX_128REG, 4            //MAX_128REG >>=4
  308.         movd    ecx, MAX_128REG
  309.         cmp     ecx, MAX_32REG
  310.         cmova   MAX_32REG,ecx            //MAX_32REG=max(max_32REG,bit32-63(MAX_128REG))


  311.         psrldq  MAX_128REG, 4            //MAX_128REG >>=4
  312.         movd    ecx, MAX_128REG
  313.         cmp     ecx, MAX_32REG
  314.         cmova   MAX_32REG,ecx            //MAX_32REG=max(max_32REG,bit64-95(MAX_128REG))

  315.         psrldq  MAX_128REG, 4            //MAX_128REG >>=4
  316.         movd    ecx, MAX_128REG
  317.         cmp     ecx, MAX_32REG
  318.         cmova   MAX_32REG,ecx            //MIN_32REG=max(max_32REG,bit96-127(MIN_128REG))

  319.         mov     END_REG, dword ptr[esp+8+PAR_PDATA]  // pData
  320.         mov     ecx, dword ptr[esp+8+PAR_LEN]        // len
  321.         lea     END_REG, [END_REG+ecx*4]             // edi=pData + len
  322.         jmp     cmp30

  323. loop3_start:
  324.         cmp    [PT_REG], MIN_32REG
  325.         cmovb  MIN_32REG,[PT_REG]

  326.         cmp    [PT_REG], MAX_32REG
  327.         cmova  MAX_32REG,[PT_REG]

  328.         add    PT_REG, 4
  329. cmp30:
  330.         cmp    PT_REG, END_REG
  331.         jb     loop3_start

  332. RET_VALUE:
  333.         mov    ecx, dword ptr[esp+8+PAR_MIN]        //*min
  334.         mov    dword ptr [ecx],MIN_32REG

  335.         mov    ecx, dword ptr[esp+8+PAR_MAX]        //*max
  336.         mov    dword ptr [ecx],MAX_32REG

  337.         emms
  338.         pop    edi
  339.         pop    esi

  340.         ret
  341.     }
  342. }
复制代码
毋因群疑而阻独见  毋任己意而废人言
毋私小惠而伤大体  毋借公论以快私情
 楼主| 发表于 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-5-13 03:42 , Processed in 0.065911 second(s), 19 queries .

Powered by Discuz! X3.5

© 2001-2024 Discuz! Team.

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