x86x64 SSE4.2 POPCNT

9,805
-1

Published on

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

Published in: Technology
1 Comment
14 Likes
Statistics
Notes
  • 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
No Downloads
Views
Total Views
9,805
On Slideshare
0
From Embeds
0
Number of Embeds
4
Actions
Shares
0
Downloads
70
Comments
1
Likes
14
Embeds 0
No embeds

No notes for slide

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 />
  1. A particular slide catching your eye?

    Clipping is a handy way to collect important slides you want to go back to later.

×