30
ビビビビビビビ (x86/x64 ビビビビビビ 1) ビビビビビ ビビビビビビ ビビ ビビ <[email protected]>

x86x64 SSE4.2 POPCNT

Embed Size (px)

DESCRIPTION

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

Citation preview

Page 1: x86x64 SSE4.2 POPCNT

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

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

サイボウズ・ラボ株式会社竹迫 良範

<[email protected]>

Page 2: x86x64 SSE4.2 POPCNT

ビットを数えるビットを数える

例題ここにいるバイナリアンの数は何人か?

机  1 0 0 1 = 0x0A ( ビット 2 個 )机  1 1 1 1 = 0x0F ( ビット 4 個 )机  1 1 1 0 = 0x0E ( ビット 3 個 )机  1 1 1 1 = 0x0F ( ビット 4 個 )机  1 1 1 1 = 0x0F ( ビット 4 個 )

合計 17 人

Page 3: x86x64 SSE4.2 POPCNT

【閑話】 Binary Hacker 中村実さんの日記【閑話】 Binary Hacker 中村実さんの日記

http://www.nminoru.jp/~nminoru/programming/bitcount.html

Page 4: x86x64 SSE4.2 POPCNT

(1) popCount 8bit If(1) popCount 8bit If

1 byte あたり 8 回条件分岐して bit を数えるfor (int i = 0; i < n; i++) {

if (*x & 0x01) c++;if (*x & 0x02) c++;if (*x & 0x04) c++;if (*x & 0x08) c++;if (*x & 0x10) c++;if (*x & 0x20) c++;if (*x & 0x40) c++;if (*x & 0x80) c++;x++;

}

Page 5: x86x64 SSE4.2 POPCNT

(2) popCount 8bit Table(2) popCount 8bit Table

256 byte のテーブルを作成して表引き

static const char popTable8bit[] = { 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, 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, 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, 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, 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, 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, 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, 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};

for (int i = 0; i < n; i++) {c += popTable8bit[(uint8)*x++];

}

Page 6: x86x64 SSE4.2 POPCNT

(3) popCount 16bit Table(3) popCount 16bit Table

64KB のテーブルを作成して表引きstatic char popTable16bit[256 * 256];

void _popCount16bitTableInit(void) { for (int i = 0; i < 256; i++) { for (int j = 0; j < 256; j++) {

popTable16bit[i*256 + j]= popTable8bit[i] + popTable8bit[j];

} }}

for (int i = 0; i < n; i++) {c += popTable18bit[(uint16)*w++];

}

Page 8: x86x64 SSE4.2 POPCNT

(4) popCount 32bit AND (SHR + ADD)(4) popCount 32bit AND (SHR + ADD)

ビットのふるいにかけながら足す

x = ((x & 0xaaaaaaaaUL) >> 1) + (x & 0x55555555UL); x = ((x & 0xccccccccUL) >> 2) + (x & 0x33333333UL); x = ((x & 0xf0f0f0f0UL) >> 4) + (x & 0x0f0f0f0fUL); x = ((x & 0xff00ff00UL) >> 8) + (x & 0x00ff00ffUL); x = ((x & 0xffff0000UL) >> 16) + (x & 0x0000ffffUL);

Page 9: x86x64 SSE4.2 POPCNT

ビットのふるいにかけながら足す( 16bit )ビットのふるいにかけながら足す( 16bit )

0xAAAA 1010 1010 1010 1010 >> 10x5555 + 0101 0101 0101 0101 ---------------------0xCCCC 1100 1100 1100 1100 >> 20x3333 + 0011 0011 0011 0011 --------------------0xF0F0 1111 0000 1111 0000 >> 40x0F0F + 0000 1111 0000 1111 ----------------0xFF00 1111 1111 0000 0000 >> 80x00FF + 0000 0000 1111 1111 ----------

Page 10: x86x64 SSE4.2 POPCNT

ビットのふるいにかけながら足す( 16bit )ビットのふるいにかけながら足す( 16bit )

0x5555 >1010 1010 1010 101 (>> 1)0x5555 + 0101 0101 0101 0101 ---------------------0x3333 >>1100 1100 1100 11 (>> 2)0x3333 + 0011 0011 0011 0011 --------------------0x0F0F >>>> 1111 0000 1111 (>> 4)0x0F0F + 0000 1111 0000 1111 ----------------0x00FF >>>> >>>> 1111 1111 (>> 8)0x00FF + 0000 0000 1111 1111 ----------

Page 11: x86x64 SSE4.2 POPCNT

(5) popCount 64bit AND (SHR + ADD)(5) popCount 64bit AND (SHR + ADD)

ビットのふるいにかけながら足す 64bit 対応

x = ((x & 0xaaaaaaaaaaaaaaaaULL) >> 1) + (x & 0x5555555555555555ULL); x = ((x & 0xccccccccccccccccULL) >> 2) + (x & 0x3333333333333333ULL); x = ((x & 0xf0f0f0f0f0f0f0f0ULL) >> 4) + (x & 0x0f0f0f0f0f0f0f0fULL); x = ((x & 0xff00ff00ff00ff00ULL) >> 8) + (x & 0x00ff00ff00ff00ffULL); x = ((x & 0xffff0000ffff0000ULL) >> 16) + (x & 0x0000ffff0000ffffULL); x = ((x & 0xffffffff00000000ULL) >> 32) // + (x & 0x00000000ffffffffULL); //

Page 12: x86x64 SSE4.2 POPCNT

(6) popCount 64bit MMX + SSE (psadbw)(6) popCount 64bit MMX + SSE (psadbw)__asm { MOVD MM0, [v+0] ;v_low PUNPCKLDQ MM0, [v+4] ;v MOVQ MM1, MM0 ;v PSRLD MM0, 1 ;v >> 1 PAND MM0, [C55] ;(v >> 1) & 0x55555555 PSUBD MM1, MM0 ;w = v - ((v >> 1) & 0x55555555) MOVQ MM0, MM1 ;w PSRLD MM1, 2 ;w >> 2 PAND MM0, [C33] ;w & 0x33333333 PAND MM1, [C33] ;(w >> 2) & 0x33333333 PADDD MM0, MM1 ;x = (w & 0x33333333) + ((w >> 2) & 0x33333333) MOVQ MM1, MM0 ;x PSRLD MM0, 4 ;x >> 4 PADDD MM0, MM1 ;x + (x >> 4) PAND MM0, [C0F] ;y = (x + (x >> 4) & 0x0F0F0F0F) PXOR MM1, MM1 ;0 PSADBW MM0, MM1 ;sum all 8 bytes (Sum of Absolute Differences) MOVD EAX, MM0 ;result in EAX per calling convention EMMS ;clear MMX state MOV retVal, EAX ;store result }

Page 13: x86x64 SSE4.2 POPCNT

http://support.amd.com/us/Processor_TechDocs/25112.PDF (pp.179-180)

Page 14: x86x64 SSE4.2 POPCNT

(7) popCount 32bit MUL (no MMX, no SSE)(7) popCount 32bit MUL (no MMX, no SSE)

__asm { MOV EAX, [v] ;v MOV EDX, EAX ;v SHR EAX, 1 ;v >> 1 AND EAX, 055555555h ;(v >> 1) & 0x55555555 SUB EDX, EAX ;w = v - ((v >> 1) & 0x55555555) MOV EAX, EDX ;w SHR EDX, 2 ;w >> 2 AND EAX, 033333333h ;w & 0x33333333 AND EDX, 033333333h ;(w >> 2) & 0x33333333 ADD EAX, EDX ;x = (w & 0x33333333) + ((w >> 2) & 0x33333333) MOV EDX, EAX ;x SHR EAX, 4 ;x >> 4 ADD EAX, EDX ;x + (x >> 4) AND EAX, 00F0F0F0Fh ;y = (x + (x >> 4) & 0x0F0F0F0F) IMUL EAX, 001010101h ;y * 0x01010101 SHR EAX, 24 ;population count = (y * 0x01010101) >> 24 MOV retVal, EAX ;store result }

Page 15: x86x64 SSE4.2 POPCNT

Intel SSE4.2 では専用命令 POPCNT が追加Intel SSE4.2 では専用命令 POPCNT が追加

http://intel.wingateweb.com/US08/published/sessions/SVRS005/SF08_SVRS005_100r.pdf

Page 16: x86x64 SSE4.2 POPCNT

Intel SSE4.2 INSTRUCTION SET - POPCNTIntel SSE4.2 INSTRUCTION SET - POPCNT

POPCNT int _mm_popcnt_u32(unsigned int

a);

POPCNT int64_t _mm_popcnt_u64(unsigned __int64

a);

#include "intrin.h"

http://softwarecommunity.intel.com/isn/Downloads/Intel%20SSE4%20Programming%20Reference.pdf

Page 17: x86x64 SSE4.2 POPCNT

(8) popCount 32bit SSE4.2 (POPCNT)(8) popCount 32bit SSE4.2 (POPCNT)

_mm_popcnt_u32()

#include "intrin.h"

size_t popCount32bitSSE42(char *x, int n){

uint32 *y = (uint32 *)x;size_t c = 0;for (int i = 0; i < n; i += 4) {

c += _mm_popcnt_u32 (*y++);}return c;

}

Page 18: x86x64 SSE4.2 POPCNT

(9) popCount 64bit SSE4.2 (POPCNT)(9) popCount 64bit SSE4.2 (POPCNT)

_mm_popcnt_u64() // ※ 32bit モードでは実行不可

#include "intrin.h"

size_t popCount32bitSSE42(char *x, int n){

uint64 *z = (uint64 *)x;size_t c = 0;for (int i = 0; i < n; i += 8) {

c += _mm_popcnt_u64 (*z++);}return c;

}

Page 19: x86x64 SSE4.2 POPCNT

【実験環境】【実験環境】

SSE4.2 (32bit/64bit)DELL Vostro DT 430 (2009 年に購入 )Core i7 860 @ 2.80 GHz (45nm Lynnfield)

MMX, SSE(1, 2, 3, 3S, 4.1, 4.2), EM64T, VT-xWindows 7 Professional (64bit)

Visual Studio 2008 (x64) 64bit C/C++ for amd64 Visual Studio 2008 (x86) 32bit C/C++ for 80x86

注意点最近の Core i5 / Core i7 の省エネ機能

Turbo Boost の機能で負荷に応じてクロックが変わる

ベンチマーク結果が不安定に

Page 20: x86x64 SSE4.2 POPCNT

Core Speed ↓×9 倍 1197.0 MHz (低負荷時)Core Speed ↓×9 倍 1197.0 MHz (低負荷時)

Page 21: x86x64 SSE4.2 POPCNT

Core Speed ↑×21 倍 2792.9 MHz (高負荷時)Core Speed ↑×21 倍 2792.9 MHz (高負荷時)

Page 22: x86x64 SSE4.2 POPCNT

BIOS で Intel® SpeedStep™ tech. を Disable にBIOS で Intel® SpeedStep™ tech. を Disable に

Page 23: x86x64 SSE4.2 POPCNT

実験結果1( 32bit 最適化 /Ox /arch:SSE2 )実験結果1( 32bit 最適化 /Ox /arch:SSE2 )

100KB 中の 1bit を数える時間(単位: K clk ) rand

0xFF 0x00

popCount8bitIf 7288.3 930.7 1941.0popCount8bitTable 210.0 208.4 210.3popCount16bitTable

114.3 104.3 107.6

popCount32bitAND

168.5 170.0 166.6

popCount64bitAND

597.1 601.7 597.1

popCount32bitMUL

280.2 279.8 276.4

popCount64bitMMX

213.9 209.8 214.4

popCount32bitSSE42

77.0 77.0 76.9

Page 24: x86x64 SSE4.2 POPCNT

実験結果1( 32bit 最適化 /Ox /arch:SSE2 )実験結果1( 32bit 最適化 /Ox /arch:SSE2 )

0

1000

2000

3000

4000

5000

6000

7000

8000

rand 0xFF 0x00

popCount8bitIf

popCount8bitTable

popCount16bitTable

popCount32bitAND

popCount64bitAND

popCount32bitMUL

popCount64bitMMX

popCount32bitSSE42

Page 25: x86x64 SSE4.2 POPCNT

実験結果1( 32bit 最適化 /Ox /arch:SSE2 )実験結果1( 32bit 最適化 /Ox /arch:SSE2 )

0

100

200

300

400

500

600

700

rand 0xFF 0x00

popCount8bitTable

popCount16bitTable

popCount32bitAND

popCount64bitAND

popCount32bitMUL

popCount64bitMMX

popCount32bitSSE42

Page 26: x86x64 SSE4.2 POPCNT

実験結果1( 32bit 最適化 /Ox /arch:SSE2 )実験結果1( 32bit 最適化 /Ox /arch:SSE2 )

0

50

100

150

200

250

300

rand 0xFF 0x00

popCount8bitTable

popCount16bitTable

popCount32bitAND

popCount32bitMUL

popCount64bitMMX

popCount32bitSSE42

Page 27: x86x64 SSE4.2 POPCNT

実験結果2( 64bit 最適化 /Ox /favor:INTEL64 )実験結果2( 64bit 最適化 /Ox /favor:INTEL64 )

100KB 中の 1bit を数える時間(単位: K clk ) rand

0xFF 0x00

popCount8bitIf 7510.7

1088.

9

2116.9

popCount8bitTable 324.1 319.5

324.7

popCount16bitTable

158.4 156.6

155.7

popCount32bitAND

182.9 173.7

188.7

popCount64bitAND

213.1 235.7

213.2

popCount32bitSSE42

85.4 88.1 80.9

popCount64bitSSE42

38.5 38.4 40.7

Page 28: x86x64 SSE4.2 POPCNT

実験結果2( 64bit 最適化 /Ox /favor:INTEL64 )実験結果2( 64bit 最適化 /Ox /favor:INTEL64 )

0

1000

2000

3000

4000

5000

6000

7000

8000

rand 0xFF 0x00

popCount8bitIf

popCount8bitTable

popCount16bitTable

popCount32bitAND

popCount64bitAND

popCount32bitSSE42

popCount64bitSSE42

Page 29: x86x64 SSE4.2 POPCNT

実験結果2( 64bit 最適化 /Ox /favor:INTEL64 )実験結果2( 64bit 最適化 /Ox /favor:INTEL64 )

0

50

100

150

200

250

300

350

rand 0xFF 0x00

popCount8bitTable

popCount16bitTable

popCount32bitAND

popCount64bitAND

popCount32bitSSE42

popCount64bitSSE42

Page 30: x86x64 SSE4.2 POPCNT

まとめまとめ

SSE4.2 POPCNT 命令が最速32bit 約 0.8 (Clk/Byte) 0.1 クロックで 1bit64bit 約 0.4 (Clk/Byte) 0.1 クロックで 2bit

x64 環境では表引きが遅くなる現象が…x86 : 8bit 約 2.1(c/B) : 16bit 約 1.1(c/B)x64 : 8bit 約 3.2(c/B) : 16bit 約 1.6(c/B)

原因のわかる人がいたら教えてください m(__)m

応用例画像処理、機械学習、パターン認識、疎行列SSE4.2 文字列命令との併用(ワードカウント)