Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

x86x64 SSE4.2 POPCNT

12,059 views

Published on

ビットを数える (x86/x64最適化勉強会1)

Published in: Technology
  • Masahiro Nakagawa氏より。ちょっと気になったので追試してみました。
    Barcelona, Bobcat, Nehalem で試した結果としては、

    1. どの Micro Architecture においてもここまで顕著な差は出ない。(8bit Table vs. 64bit POPCNT で 4倍ちょいがいいところ @ Nehalem)
    2. Table Lookup は L2 Cache の量に左右されるが (手元では 128MiB で試験した)、Barcelona, Nehalem 共に x86, x64間で誤差以上の差を認めず。
    3. /favor Option は今はほとんど意味がないはず…。

    → 測定方法に問題があると考える。いかがでしょうか?

    コンパイル環境
    Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.30319.01
    ; Function compile flags: /Ogtpy
       Reply 
    Are you sure you want to  Yes  No
    Your message goes here

x86x64 SSE4.2 POPCNT

  1. 1. ビットを数える(x86/x64最適化勉強会1)<br />サイボウズ・ラボ株式会社竹迫 良範<takesako@x86.cx><br />
  2. 2. ビットを数える<br />例題<br />ここにいるバイナリアンの数は何人か?<br />机 1 0 0 1 = 0x0A (ビット2個)<br />机 1 1 1 1 = 0x0F (ビット4個)<br />机 1 1 1 0 = 0x0E (ビット3個)<br />机 1 1 1 1 = 0x0F (ビット4個)<br />机 1 1 1 1 = 0x0F (ビット4個)<br />合計 17人<br />
  3. 3. 【閑話】Binary Hacker 中村実さんの日記<br />http://www.nminoru.jp/~nminoru/programming/bitcount.html<br />
  4. 4. (1) popCount 8bit If<br />1 byte あたり 8回条件分岐して bit を数える<br /> for (int i = 0; i < n; i++) {<br /> if (*x & 0x01) c++;<br /> if (*x & 0x02) c++;<br /> if (*x & 0x04) c++;<br /> if (*x & 0x08) c++;<br /> if (*x & 0x10) c++;<br /> if (*x & 0x20) c++;<br /> if (*x & 0x40) c++;<br /> if (*x & 0x80) c++;<br /> x++;<br />}<br />
  5. 5. (2) popCount 8bit Table<br />256 byte のテーブルを作成して表引き<br />static const char popTable8bit[] = {<br /> 0,1,1,2,1,2,2,3,1,2,2,3,2,3,3,4,1,2,2,3,2,3,3,4,2,3,3,4,3,4,4,5,<br /> 1,2,2,3,2,3,3,4,2,3,3,4,3,4,4,5,2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,<br /> 1,2,2,3,2,3,3,4,2,3,3,4,3,4,4,5,2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,<br /> 2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,3,4,4,5,4,5,5,6,4,5,5,6,5,6,6,7,<br /> 1,2,2,3,2,3,3,4,2,3,3,4,3,4,4,5,2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,<br /> 2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,3,4,4,5,4,5,5,6,4,5,5,6,5,6,6,7,<br /> 2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,3,4,4,5,4,5,5,6,4,5,5,6,5,6,6,7,<br /> 3,4,4,5,4,5,5,6,4,5,5,6,5,6,6,7,4,5,5,6,5,6,6,7,5,6,6,7,6,7,7,8<br />};<br />for (int i = 0; i < n; i++) {<br /> c += popTable8bit[(uint8)*x++];<br />}<br />
  6. 6. (3) popCount 16bit Table<br />64KB のテーブルを作成して表引き<br />static char popTable16bit[256 * 256];<br />void _popCount16bitTableInit(void) { <br /> for (int i = 0; i < 256; i++) {<br /> for (int j = 0; j < 256; j++) {<br /> popTable16bit[i*256 + j]<br />= popTable8bit[i] + popTable8bit[j];<br /> }<br /> }<br />}<br />for (int i = 0; i < n; i++) {<br /> c += popTable18bit[(uint16)*w++];<br />}<br />
  7. 7. http://www.amazon.co.jp/dp/4434046683<br />http://www.hackersdelight.org/<br />
  8. 8. (4) popCount 32bit AND(SHR + ADD)<br />ビットのふるいにかけながら足す<br /> x = ((x & 0xaaaaaaaaUL) >> 1) <br /> + (x & 0x55555555UL); <br /> x = ((x & 0xccccccccUL) >> 2) <br /> + (x & 0x33333333UL); <br /> x = ((x & 0xf0f0f0f0UL) >> 4) <br /> + (x & 0x0f0f0f0fUL); <br /> x = ((x & 0xff00ff00UL) >> 8) <br /> + (x & 0x00ff00ffUL); <br /> x = ((x & 0xffff0000UL) >> 16) <br /> + (x & 0x0000ffffUL);<br />
  9. 9. ビットのふるいにかけながら足す(16bit)<br />0xAAAA 1010 1010 1010 1010 >> 1<br />0x5555 + 0101 0101 0101 0101 <br />---------------------<br />0xCCCC 1100 1100 1100 1100 >> 2<br />0x3333 + 0011 0011 0011 0011 <br />--------------------<br />0xF0F0 1111 0000 1111 0000 >> 4<br />0x0F0F + 0000 1111 0000 1111 <br />----------------<br />0xFF00 1111 1111 0000 0000 >> 8<br />0x00FF + 0000 0000 1111 1111<br />----------<br />
  10. 10. ビットのふるいにかけながら足す(16bit)<br />0x5555 >1010 1010 1010 101 (>> 1)<br />0x5555 + 0101 0101 0101 0101 <br />---------------------<br />0x3333 >>1100 1100 1100 11 (>> 2)<br />0x3333 + 0011 0011 0011 0011 <br />--------------------<br />0x0F0F >>>> 1111 0000 1111 (>> 4)<br />0x0F0F + 0000 1111 0000 1111 <br />----------------<br />0x00FF >>>> >>>> 1111 1111 (>> 8)<br />0x00FF + 0000 0000 1111 1111<br />----------<br />
  11. 11. (5) popCount 64bit AND (SHR + ADD)<br />ビットのふるいにかけながら足す64bit対応<br /> x = ((x & 0xaaaaaaaaaaaaaaaaULL) >> 1) <br /> + (x & 0x5555555555555555ULL); <br /> x = ((x & 0xccccccccccccccccULL) >> 2) <br /> + (x & 0x3333333333333333ULL); <br /> x = ((x & 0xf0f0f0f0f0f0f0f0ULL) >> 4) <br /> + (x & 0x0f0f0f0f0f0f0f0fULL); <br /> x = ((x & 0xff00ff00ff00ff00ULL) >> 8) <br /> + (x & 0x00ff00ff00ff00ffULL); <br /> x = ((x & 0xffff0000ffff0000ULL) >> 16) <br /> + (x & 0x0000ffff0000ffffULL); <br /> x = ((x & 0xffffffff00000000ULL) >> 32) //<br /> + (x & 0x00000000ffffffffULL); //<br />
  12. 12. (6) popCount 64bit MMX + SSE (psadbw)<br />__asm { <br /> MOVD MM0, [v+0] ;v_low<br />PUNPCKLDQ MM0, [v+4] ;v <br /> MOVQ MM1, MM0 ;v <br /> PSRLD MM0, 1 ;v >> 1 <br /> PAND MM0, [C55] ;(v >> 1) & 0x55555555 <br /> PSUBD MM1, MM0 ;w = v - ((v >> 1) & 0x55555555) <br /> MOVQ MM0, MM1 ;w <br /> PSRLD MM1, 2 ;w >> 2 <br /> PAND MM0, [C33] ;w & 0x33333333 <br /> PAND MM1, [C33] ;(w >> 2) & 0x33333333 <br /> PADDD MM0, MM1 ;x = (w & 0x33333333) + ((w >> 2) & 0x33333333) <br /> MOVQ MM1, MM0 ;x <br /> PSRLD MM0, 4 ;x >> 4 <br /> PADDD MM0, MM1 ;x + (x >> 4) <br /> PAND MM0, [C0F] ;y = (x + (x >> 4) & 0x0F0F0F0F) <br /> PXOR MM1, MM1 ;0 <br /> PSADBW MM0, MM1 ;sum all 8 bytes (Sum of Absolute Differences)<br /> MOVD EAX, MM0 ;result in EAX per calling convention <br /> EMMS ;clear MMX state <br /> MOV retVal, EAX ;store result <br />}<br />
  13. 13. http://support.amd.com/us/Processor_TechDocs/25112.PDF (pp.179-180)<br />
  14. 14. (7) popCount 32bit MUL (no MMX, no SSE)<br /> __asm { <br /> MOV EAX, [v] ;v <br /> MOV EDX, EAX ;v <br /> SHR EAX, 1 ;v >> 1 <br /> AND EAX, 055555555h ;(v >> 1) & 0x55555555 <br /> SUB EDX, EAX ;w = v - ((v >> 1) & 0x55555555) <br /> MOV EAX, EDX ;w <br /> SHR EDX, 2 ;w >> 2 <br /> AND EAX, 033333333h ;w & 0x33333333 <br /> AND EDX, 033333333h ;(w >> 2) & 0x33333333 <br /> ADD EAX, EDX ;x = (w & 0x33333333) + ((w >> 2) & 0x33333333)<br /> MOV EDX, EAX ;x <br /> SHR EAX, 4 ;x >> 4 <br /> ADD EAX, EDX ;x + (x >> 4) <br /> AND EAX, 00F0F0F0Fh ;y = (x + (x >> 4) & 0x0F0F0F0F) <br /> IMUL EAX, 001010101h ;y * 0x01010101 <br /> SHR EAX, 24 ;population count = (y * 0x01010101) >> 24 <br /> MOV retVal, EAX ;store result <br /> } <br />
  15. 15. Intel SSE4.2 では専用命令 POPCNT が追加<br />http://intel.wingateweb.com/US08/published/sessions/SVRS005/SF08_SVRS005_100r.pdf<br />
  16. 16. Intel SSE4.2 INSTRUCTION SET- POPCNT<br />#include "intrin.h"<br />POPCNT int _mm_popcnt_u32(unsigned int a);<br />POPCNT int64_t _mm_popcnt_u64(unsigned __int64 a);<br />http://softwarecommunity.intel.com/isn/Downloads/Intel%20SSE4%20Programming%20Reference.pdf<br />
  17. 17. (8) popCount 32bit SSE4.2 (POPCNT)<br />_mm_popcnt_u32()<br />#include "intrin.h"<br />size_tpopCount32bitSSE42(char *x, int n)<br />{<br /> uint32 *y = (uint32 *)x;<br />size_t c = 0;<br /> for (int i = 0; i < n; i += 4) {<br /> c += _mm_popcnt_u32 (*y++);<br /> }<br /> return c;<br />}<br />
  18. 18. (9) popCount 64bit SSE4.2 (POPCNT)<br />_mm_popcnt_u64() // ※ 32bitモードでは実行不可<br />#include "intrin.h"<br />size_t popCount32bitSSE42(char *x, int n)<br />{<br />uint64 *z = (uint64 *)x;<br />size_t c = 0;<br /> for (int i = 0; i < n; i += 8) {<br /> c += _mm_popcnt_u64 (*z++);<br /> }<br /> return c;<br />}<br />
  19. 19. 【実験環境】<br />SSE4.2 (32bit/64bit)<br />DELL Vostro DT 430 (2009年に購入)<br />Core i7 860 @ 2.80 GHz (45nm Lynnfield)<br />MMX, SSE(1, 2, 3, 3S, 4.1, 4.2), EM64T, VT-x<br />Windows 7 Professional (64bit)<br />Visual Studio 2008 (x64) 64bit C/C++ for amd64<br />Visual Studio 2008 (x86) 32bit C/C++ for 80x86<br />注意点<br />最近の Core i5 / Core i7 の省エネ機能<br />Turbo Boost の機能で負荷に応じてクロックが変わる<br />ベンチマーク結果が不安定に<br />
  20. 20. Core Speed ↓×9倍1197.0 MHz(低負荷時)<br />
  21. 21. Core Speed ↑×21倍 2792.9 MHz(高負荷時)<br />
  22. 22. BIOS で Intel® SpeedStep™ tech. を Disable に<br />
  23. 23. 実験結果1(32bit 最適化/Ox/arch:SSE2)<br />100KB 中の1bitを数える時間(単位:K clk)<br />
  24. 24. 実験結果1(32bit 最適化/Ox/arch:SSE2)<br />
  25. 25. 実験結果1(32bit 最適化/Ox/arch:SSE2)<br />
  26. 26. 実験結果1(32bit 最適化/Ox/arch:SSE2)<br />
  27. 27. 実験結果2(64bit 最適化/Ox/favor:INTEL64)<br />100KB 中の1bitを数える時間(単位:K clk)<br />
  28. 28. 実験結果2(64bit 最適化/Ox/favor:INTEL64)<br />
  29. 29. 実験結果2(64bit 最適化/Ox/favor:INTEL64)<br />
  30. 30. まとめ<br />SSE4.2 POPCNT命令が最速<br />32bit 約0.8 (Clk/Byte)  0.1クロックで1bit<br />64bit 約0.4 (Clk/Byte) 0.1クロックで2bit<br />x64 環境では表引きが遅くなる現象が…<br />x86:8bit 約2.1(c/B):16bit約1.1(c/B)<br />x64:8bit 約3.2(c/B):16bit約1.6(c/B)<br />原因のわかる人がいたら教えてください m(__)m<br />応用例<br />画像処理、機械学習、パターン認識、疎行列<br />SSE4.2文字列命令との併用(ワードカウント)<br />

×