SlideShare a Scribd company logo
1 of 41
Download to read offline
SSE4.2の文字列処理命令の紹介


                     Cybozu Labs
           2011/8/6 光成滋生(8/23加筆修正p3, p.25)
                  x86/x64最適化勉強会#1


2011/8/6                  /41                1
内容
SIMD向き/不向き
SSE2によるstrlen
SSE4.2の文字列処理命令
SSE4.2によるstrlen
intrinsic命令
単語を数える
改良
まとめ


2011/8/6           2 /41
説明とコード
Intelプロセッサ最適化マニュアルを読もう
  http://homepage1.nifty.com/herumi/prog/intel-opt.html
コード片
  https://github.com/herumi/opti/
アライメントとページ境界に関する補足(必読)
  http://homepage1.nifty.com/herumi/diary/1108.html#8




2011/8/6                                             3 /41
SIMD向き/不向き
SIMDは4個(or 8/16)個のデータを同時に同じよう
 に処理するためのもの
SIMD向き
  先程の最大値を求める場合,4個ずつやってもよかった
  複数個同時に加算,掛け算,etc.
  これらの処理は概ね得意
SIMD向きでないもの
  本質的に分岐が多いもの
  一つ前の状態に依存するもの


2011/8/6                    4 /41
SSE2によるstrlen
素朴なstrlen
           size_t strlenC(const char *p)
           {
               size_t len = 0;
               while (p[len]) len++;
               return len;
           }

SIMDを使うアイデア
  16byteずつデータを取得してbyte単位で0と比較する
     0があれば0xff, なければ0をbyte単位に取得できる
  上記データの最上位ビット(MSB)をかき集める
  下から数えて初めて1になった場所があればそこが'¥0'
2011/8/6                                   5 /41
SIMDを使うアイデア
 xm0に文字列, xm1に0を入れておいてbyte比較
      pcmpeqb xm0, xm1
           xm0   +0   +1   +2   +3   +4   +5     +6   +7
           xm0   h    e    l    l    o    '¥0'   ?    ?
           xm1   0    0    0    0    0     0     0    0
           xm0   0    0    0    0    0    0xff   0    0

 xm0のMSBをかき集める
      pmovmskb eax, xm0
      1bitずつ16個合計16bitのデータになる
      eax = 0b00100000
 eaxが0で無ければ下から初めて1になる場所を探す
      bsf eax, eax ; eax = 5となる
2011/8/6                                                   6 /41
実装
https://github.com/herumi/opti/のstrlen_sse2.cpp
  実際には16byteアライメントされていないと扱いにくいた
   めループ開始前に端数処理がある
  当時3年前(gcc 4.3やVC9に対して)は速かった
  最近のgcc(4.4以降?)は同様の手法やこれから述べる
   SSE4.2の命令が用いられている
     ただしgcc 4.6でもstrlenCがそういうコードになるわけではない
       あくまでもライブラリ関数で使われているだけ
まとめ
  アライメント処理 / byte単位での照合 / ビットスキャン


2011/8/6                                     7 /41
SSE4.2の文字列処理命令
超複雑
  今述べたアライメントの処理(不要になった),
   byte単位での照合(より高機能),
   ビットスキャンを1命令で行う
pcmpXstrY xm0, xm1/mem, imm8
  imm8でさまざまなモードを指定する
  memは16byte alignmentされていなくてもよい
                   0終端による文字列     edx/eaxによる長さ指定文字列
  処理の結果をスキャンして       pcmpistri        pcmpestri
  ecxに出力
  処理の結果をそのままxm0に     pcmpistrm        pcmpestrm
  出力

2011/8/6                                          8 /41
長さ指定モード
pcmpestri, pcmpestrmは明示的な長さを指定する
pcmpestrY xm0, xm1, imm8
  eaxはxm0の文字列の長さ
  edxはxm1の文字列の長さ




2011/8/6                        9 /41
imm8の内容
imm8[0](最下位ビット)
  0なら入力データをbyte単位とみなす
   1ならword(2byte)単位と見なして処理
imm8[1]
  0なら符号無しデータ
   1なら符号ありデータとして処理
imm8[3:2]
  文字列の比較方法を選択する
     00b(equal array) マッチする最初の文字を探す
     01b(ranges) 範囲内から文字列を探す
     10b(equal each) 文字列比較をする
     11b(equal ordered) 部分文字列比較をする
2011/8/6                               10 /41
equal anyの疑似コード
set : 検索したい文字集合
text : 検索対象のテキスト
           for (int j = 0; j < len; j++) {
               int tmp = 0;
               for (int i = 0; i < len; i++) {
                   tmp |= text[j] == set[i];
               }
               IntRes1[j] = tmp;
           }

set = "abc", text = "xaybzc";
  IntRes1[0] = setのどれもtext[0]にマッチしないのでfalse
  IntRes1[1] = setのaがtext[1]にマッチするのでtrue
  ...
2011/8/6                                         11 /41
rangesの疑似コード
str:[2*i+0] : 文字列範囲の始め
str[2*i+1] : 文字列範囲の終わり
 for (int j = 0; j < len; j++) {
   int tmp = 0;
   for (int i = 0; i < len; i += 2) {
     tmp |= (str[i] <= text[j]) && (text[j] <= str[i + 1]);
   }
   IntRes1[j] = tmp;
 }

str = "az09", text = "0Abc";
  IntRes1[0] = text[0]は[0-9]にあるのでtrue
  IntRes1[1] = text[1]はどこにも入らないのでfalse
  IntRes1[2] = text[2]は[a-z]にあるのでtrue
2011/8/6                                                      12 /41
equal eachの疑似コード
str : 検索したい文字列
text : 検索対象のテキスト
 for (int j = 0; j < len; j++) {
   IntRes1[j] = text[j] == str[j];
 }

str = "abc", text = "aXc";
  IntRes1[0] = text[0] == aなのでtrue
  IntRes1[1] = text[1] != bなのでfalse
  IntRes1[2] = text[2] == cなのでtrue



2011/8/6                               13 /41
equal orderedの疑似コード
str : 検索したい文字列
text : 検索対象のテキスト
 for (int j = 0; j < len; j++) {
   int tmp = 1;
   for (int i = 0; i < len - j; i++) {
     tmp &= text[j + i] == str[i];
   }
   IntRes1[j] = tmp;
 }


これは複雑なのでinvalid文字列の扱いを説明して
 から

2011/8/6                                 14 /41
imm8[5:4]
中間結果(IntRes1)に作用してIntRes2を作る
00b(positive polarity)
  そのまま出力(IntRes2[i] = IntRes1[i])
01b(negative polarity)
  反転して出力(IntRes2[i] = ~IntRes1[i])
10b(masked(+))
  そのまま
01b(masked(-))
  入力がinvalidならそのまま,そうでなければ反転


2011/8/6                              15 /41
imm8[6]
最終結果操作
  IntRes2からecxかxm0に出力するデータを作る
pcmpestri, pcmpistriに対する設定
  0ならIntRes2のLSBが入る
  1ならIntRes2のMSBが入る
     ただしIntRes2 == 0なら16(byteのとき)か8(wordのとき)
pcmpestrm, pcmpistrmに対する設定
  0ならIntRes2が0拡張されてxmm0に入る.
  1ならIntRes2の各ビットが入力データの型の大きさにした
   がってbyteかword単位のマスクに拡張されてxmm0に入
   る.
2011/8/6                                        16 /41
invalid文字の扱い
invalid文字
  文字列の比較において,途中に'¥0'が出たり,長さが短く
   て終わってしまったときの残りの文字たちのこと
invalid文字と通常の文字との演算ルール
    xm1(str)   xm2(text)   equal any   ranges   equal each   equal
                                                             orderd
      valid      valid     通常通り        通常通り      通常通り        通常通り
      valid     invalid     常に0        常に0        常に0        常に0
     invalid     valid      常に0        常に0        常に0        常に1
     invalid    invalid     常に0        常に0        常に1        常に1




2011/8/6                                                              17 /41
equal orderedの例
src = "ABCA¥0XYZ", text = "BABCAB¥0S";
                        text文字列
                         7    6    5     4     3     2    1    0(j)
                        S    ¥0    B     A     C     B    A     B
      src   0(i)   A    fF   fF    F     T     F     F    T     F       F
      文      1     B    fF   fF    T     F     F     T    F     x       T
      字                                                                 F
      列      2     C    fF   fF    F     F     T     F    x     x       F
             3     A    fF   fF    F     T     F     x    x     x       F
                                                                        F
             4     ¥0   fT   fT    fT    fT    fT    x    x     x
                                                                        F
             5     X    fT   fT    fT    x     x     x    x     x       F
             6     Y    fT   fT    x     x     x     x    x     x     IntRes1
             7     Z    fT    x    x     x     x     x    x     x

'¥0'の後ろはinvalid http://journal.mycom.co.jp/articles/2008/04/10/idf09/008.html
T:True, F:False, fT:force True, fF:force Flase
2011/8/6                                                                    18 /41
フラグレジスタ
フラグレジスタは次のように変化する
  pcmpXstrY xm0, xm1, imm8

           pcmpestri / pcmpestrm           pcmpistri / pcmpistrm
    CF                             IntRes2 != 0
    ZF          |edx| < 16         xm1のいずれかが0なら1, そうでなければ0
    SF          |eax| < 16         xm0のいずれかが0なら1, そうでなければ0
    OF                              IntRes2[0]




2011/8/6                                                           19 /41
SSE4.2を使ったstrlen
pcmpistriを選択
  0終端文字列を扱い,位置をビットスキャンで取る
imm8[1:0] = 0
  符号無しbyte単位
'¥0'を探す
  1~255にマッチしないものを見つけると考える
  範囲指定なのでrangesを使う(imm8[3:2] = 01b)
  中間結果操作でビット反転をするのでimm8[5:4]=01b
  最終結果の下から初めての1を見つけるのでimm8[6]=0
よってimm8=0b010100=0x14となる
16byteの中に'¥0'がなければZF = 0
2011/8/6                          20 /41
SSE4.2を使ったstrlen
よってコードは次のようになる
  とても簡単
   mov(eax, 0xff01); // { '0x01', '0xff', '¥0' };
   movd(xm0, eax); // xm0にrangesの文字列を設定
   mov(a, p); // 文字列の先頭
   jmp(".in");
 L("@@");
   add(a, 16);
 L(".in");
   pcmpistri(xm0, ptr [a], 0x14); // 比較して
   jnz("@b");
   add(a, c);
   sub(a, p);
   ret();



2011/8/6                                            21 /41
ベンチマーク
 ランダムな長さの文字列に対するbyteあたりの処理時間
       文字列平均長        5.04   66.62   261.78   1063.83
            glibc    5.70   0.58     0.27     0.20
           strlenC   6.92   1.89     1.41     1.24
           SSE2      5.74   0.54     0.21     0.15
           SSE4.2    5.03   0.69     0.29     0.21


 SSE2バージョンは文字列長が長いと速い
      32byte単位で処理しているため
 glibcとSSE4.2は大体同じ
      短いところでglibcが若干速いのは短いとき用の処理が入ってる
       から



2011/8/6                                               22 /41
注意
  初期のintelのマニュアルは遅いバージョンが載っていた
     http://journal.mycom.co.jp/articles/2008/04/10/idf09/008.html

 L("@@");
   add(a, ecx); // 16でなくてecx
 L(".in");
   pcmpistri(xm0, ptr [a], 0x14); // 比較して
   jnz("@b");

  これではpcmpistriの出力結果のecxが確定するまでadd
   で値を計算できないためスループットが下がるため




2011/8/6                                                        23 /41
intrinsic版の注意点
pcmpistriなどの命令はecx(またはxm0)とフラグの
 両方を出力するがintrinsicは一つしか値をとれない
  個別の値を取る関数が用意されている
       返り値           pcmpestri      pcmpestrm       pcmpistri       pcmpistrm
     ecx/xmm0       _mm_cmpestri   _mm_cmpestrm   _mm_cmpistri    _mm_cmpistrm
 CF = 0 && ZF = 0          _mm_cmpestra                    _mm_cmpistra
           CF              _mm_cmpestrc                    _mm_cmpistrc
           OF              _mm_cmpestro                    _mm_cmpistro
           SF              _mm_cmpestrs                    _mm_cmpistrs
           ZF              _mm_cmpestrz                    _mm_cmpistrz

  _mm_cmpestraなどはAFを取得するものではない
     AFは常に0に設定される
     またhttp://msirocoder.blog35.fc2.com/blog-entry-65.html
      で触れられているようなResetでも無いように思う(自信無し)
2011/8/6                                                                    24 /41
SSE4.2 intrinsic版strlen
 #ifdef _WIN32            *(__m128i*)pはmovdqaが生成される可能性がある
   #include <intrin.h>    _mm_loadu_si128((__m128i*)p)を使うべき
 #else                    (他のスライドも同様)
                          cf.
   #include <x86intrin.h> http://homepage1.nifty.com/herumi/diary/1108.html#8
 #endif
 size_t strlenSSE42_C(const char* top)
 {
     const __m128i im = _mm_set1_epi32(0xff01);
     const char *p = top - 16;
     do {
          p += 16;
     } while (!_mm_cmpistrz(im, *(__m128i*)p, 0x14)); // ZF
     p += _mm_cmpistri(im, *(__m128i*)p, 0x14); // get ecx
     return p - top;
 }
msiroさんのwhile()の方が簡潔でよさそう
  asmコードと実行時間は同じようなものになる
2011/8/6                                                                 25 /41
生成コード(by gcc 4.6.0)
     lea rdx, [rdi-16]
     movdqa xmm0, XMMWORD PTR .LC0[rip]
     jmp .L11
   // align16
 .L12:
     mov rdx, rax
 .L11:
     lea rax, [rdx+16]
     pcmpistri    xmm0, XMMWORD PTR [rdx+16], 20
     jne .L12

_mm_cmpistrzと_mm_cmpistriの両方が一つの
 pcmpistriにまとめられている
  よかった.たいしたもんだ

2011/8/6                                           26 /41
strchr by SSE4.2
ナイーブな実装
 const char *strchr_C(const char *p, int c) {
   while (*p) {
     if (*p == (char)c) return p;
     p++;
   }
   return 0;
 }

imm8の選択
  符号無しbyte単位でimm8[1:0] = 0
  集計はequal anyでimm8[3:2] = 0
     文字列は"(char)c";
  中間結果と最終結果は何もしないimm8[6:5:4] = 0
2011/8/6                                        27 /41
何のフラグでループするか
IntRes2 != 0なら文字を発見したのでループ脱出
  CFを確認する
次に文字列が終了したかをZFで確認する
 #lp
   pcmpistri xm0, ptr [p], 0
   jc #found
   jnz #lp


CF = 0 && ZF = 0のとき,すなわちjaとすればよい
 #lp
   p += 16
   pcmpistri xm0, ptr [p], 0
   ja #lp
   jnc #notfound
 #found
2011/8/6                       28 /41
コードとベンチマーク

 const char *strchrSSE42_C(const char* p, int c)
 {
     const __m128i im = _mm_set1_epi32(c & 0xff);
     while (_mm_cmpistra(im, *(const __m128i*)p, 0)) {
         p += 16;
     }
     if (_mm_cmpistrc(im, *(const __m128i*)p, 0)) {
         return p + _mm_cmpistri(im, *(const __m128i*)p, 0);
     }
     return 0;
 }


           Xeon         strchrLIB     strchr_C     strchrSSE42
           clk           0.459          3.012         0.252


2011/8/6                                                         29 /41
範囲指定への拡張
 const char *findRange_C(const char* p, char c1, char c2) {
     const unsigned char *up = (const unsigned char *)p;
     unsigned char uc1 = c1;
     unsigned char uc2 = c2;
     while (*up) {
         if (uc1 <= *up && *up <= uc2) return (const char*)up;
         up++;
     }
     return 0;
 }

if ((unsigned char)(*p - c1) <= (unsigned char)(c2
 - c1)) return p;
 により1割ぐらい早くなるが
SSE4.2版はequal anyをrangesにするだけ
2011/8/6                                                         30 /41
findRange by SSE4.2
 const char *findRange_SSE42(const char* p, char c1, char c2) {
     const __m128i im = _mm_set1_epi32(
       ((unsigned char)c1) | (((unsigned char)c2) << 8)
     );
     while (_mm_cmpistra(im, *(const __m128i*)p, 4)) {
         p += 16;
     }
     if (_mm_cmpistrc(im, *(const __m128i*)p, 4)) {
         return p + _mm_cmpistri(im, *(const __m128i*)p, 4);
     }
     return 0;
 }


           i7       findRange_C     findRange2_C    findRange_SSE42
           clk         2.310            2.055            0.214


2011/8/6                                                          31 /41
単語のカウント
ここでは英数字とアポストロフィの連続を単語とし,
 その個数を数える(インテルのマニュアルより)
インテルの比較用Cコードはかなりトリッキー
  だが,凄く速いというわけでもない(おもしろいけど)
     size_t countWord_C(const char *p) {
       static const char alp_map8[32] = {
          0, 0, 0, 0, 0x80, 0, 0xff, 0x3, 0xfe, 0xff, 0xff, 0x7, 0xfe, 0xff, 0xff, 0x7 };
       size_t i = 1, cnt = 0;
       unsigned char cc, cc2;
       bool flag[3];
       cc2 = cc = p[0];
       flag[1] = alp_map8[cc >> 3] & (1 << (cc & 7));
       while (cc2) {
         cc2 = p[i];
         flag[2] = alp_map8[cc2 >> 3] & (1 << (cc2 & 7));
         if (!flag[2] && flag[1]) cnt++;
         flag[1] = flag[2];
         i++;
       }
       return cnt;
     }
2011/8/6                                                                                    32 /41
多少最適化したもの
今回はこれを使う(2倍程度速い)
 static char alnumTbl2[256]; // 単語になる文字だけ1, それ以外は0
 size_t countWord_C2(const char *p){
   size_t count = 0;
   unsigned char c = *p++;
   char prev = alnumTbl2[c];
   while (c) {
     c = *p++;
     char cur = alnumTbl2[c];
     if (!cur && prev) {
       count++;
     }
     prev = cur;
   }
   return count;
 }

2011/8/6                                             33 /41
SSE4.2 intrinsc版countWord
msiroさんのを少し変更
 MIE_ALIGN(16) static const char alnumTbl[16] = {
   '¥'', '¥'', '0', '9', 'A', 'Z', 'a', 'z', '¥0' };
 size_t countWord_SSE42(const char *p) {
 const __m128i im = *(const __m128i*)alnumTbl;
   __m128i ret, x, prev = _mm_setzero_si128();
   size_t count = 0; goto SKIP;
   do {
     p += 16;
   SKIP:
     ret = _mm_cmpistrm(im, *(const __m128i*)p, 0x4);
     x = _mm_slli_epi16(ret, 1);
     x = _mm_or_si128(prev, x);
     prev = _mm_srli_epi32(ret, 15);
     x = _mm_xor_si128(x, ret);
     count += _mm_popcnt_u32(_mm_cvtsi128_si32(x));
   } while (!_mm_cmpistrz(im, *(const __m128i*)p, 0x4));
   return count / 2;
 }
2011/8/6                                                   34 /41
countWord_SSE42の解説
', 0-9, A-Z, a-zの範囲にあるもの(C)を探す
  rangesを使う
(C)とそれ以外の境界は0から1, 1から0に変化する
  エッジは1bitずらしてxorすれば検出できる

                   1        1       1       0       0       1       0       0


           1       1        1       0       0       1       0       0       0

次回使う                                    xor
               0        0       1       0       1       1       0       0

                       ビットの立っている個数を数える
                       倍数えることになるので最後に2で割る
2011/8/6                                                                        35 /41
SSE4.2 intrinsc版countWord
(C)に入る範囲を配列で指定する
 MIE_ALIGN(16) static const char alnumTbl[16] = {
   '¥'', '¥'', '0', '9', 'A', 'Z', 'a', 'z', '¥0'
 };

 const __m128i im = *(const __m128i*)alnumTbl;

符号無しbyte単位なのでimm8[1:0] = 0
集計方法はranges imm8[3:2] = 01b
中間操作:そのまま出力 imm8[5:4] = 0
最終操作:そのまま出力 imm8[6] = 0
よってimm8 = 0b100 = 4

2011/8/6                                            36 /41
SSE4.2 intrinsc版countWord
do {
  p += 16;
  ret = _mm_cmpistrm(im, *(const __m128i*)p, 0x4);
  x = _mm_slli_epi16(ret, 1);
  x = _mm_or_si128(prev, x);
  prev = _mm_srli_epi32(ret, 15);
  x = _mm_xor_si128(x, ret);
  count += _mm_popcnt_u32(_mm_cvtsi128_si32(x));
} while (!_mm_cmpistrz(im, *(const __m128i*)p, 0x4));
retの各ビットに(c in (C)) ? 1 : 0が入る
x = ret << 1;
x = x | prev; // 前回の残りの1bit
prev = ret >> 15;
x = x ^ ret.
count += xのビットの数
文字列の中に'¥0'が見つかるまでループ
2011/8/6                                                37 /41
ベンチマーク
           i7      インテルオリジナル           改良版         intrinsic版
           clk           854            456           42

 一桁速い!                            L("@@");
                                     add(p, 16);
 生成コードを見てみる                         movdqa(xm1, ptr [p]);
      あれ,pcmpistrmが2回               pcmpistrm(xm2, xm1, 4);
                                     movdqa(xm4, xm0);
   pcmpistrmはフラグを変える                psllw(xm4, 1);
                                     por(xm4, xm3);
      真ん中のadd(a, d);が邪魔
                                     pxor(xm4, xm0);
      lea(a, ptr [a + d]);にすればOK?   movd(d, xm4);
                                     movdqa(xm3, xm0);
                                     popcnt(d, d);
                                     add(a, d);
                                     psrld(xm3, 15);
                                     pcmpistrm(xm2, xm1, 4);
                                     jnz("@b");
2011/8/6                                                        38 /41
改良
 実はpopcntもフラグをいじる   L("@@");
     命令の順序を入れ換えてしまおう movdqa(xm4, xm0);
                       psllw(xm4, 1);
                       por(xm4, xm3);
                       movdqa(xm3, xm0);
                       pxor(xm4, xm0);
                       psrld(xm3, 15);
                       movd(d, xm4);
                       popcnt(d, d);
                       add(p, 16);
   速くなった!             add(a, d);
                       pcmpistrm(xm2, ptr [p], 4);
                       jnz("@b");

       i7   インテルオリジナル   改良版    intrinsic版   改良版
      clk      854      456       42        33


2011/8/6                                          39 /41
改悪?
 Xeonでは遅くなっていた
       i7   インテルオリジナル   改良版   intrinsic版   改良版
      clk      854      456      42         33

    Xeon    インテルオリジナル   改良版   intrinsic版   改良版?
      clk      1714     874      46         53
 いろいろ難しい…




2011/8/6                                          40 /41
まとめ
SSE4.2の命令の紹介
  アライメントを気にする必要がない
  超高機能な文字マッチパターン
  bsf, bsr相当の機能も持つ
  intrinsic版はフラグとecx/xm0の両方必要
     手動最適化の余地あり?
  プロセッサによって結構性能が違うこともある




2011/8/6                         41 /41

More Related Content

What's hot

何となく勉強した気分になれるパーサ入門
何となく勉強した気分になれるパーサ入門何となく勉強した気分になれるパーサ入門
何となく勉強した気分になれるパーサ入門masayoshi takahashi
 
多段階計算の型システムの基礎
多段階計算の型システムの基礎多段階計算の型システムの基礎
多段階計算の型システムの基礎T. Suwa
 
いまさら聞けない!CUDA高速化入門
いまさら聞けない!CUDA高速化入門いまさら聞けない!CUDA高速化入門
いまさら聞けない!CUDA高速化入門Fixstars Corporation
 
きつねさんでもわかるLlvm読書会 第2回
きつねさんでもわかるLlvm読書会 第2回きつねさんでもわかるLlvm読書会 第2回
きつねさんでもわかるLlvm読書会 第2回Tomoya Kawanishi
 
計算機アーキテクチャを考慮した高能率画像処理プログラミング
計算機アーキテクチャを考慮した高能率画像処理プログラミング計算機アーキテクチャを考慮した高能率画像処理プログラミング
計算機アーキテクチャを考慮した高能率画像処理プログラミングNorishige Fukushima
 
AVX-512(フォーマット)詳解
AVX-512(フォーマット)詳解AVX-512(フォーマット)詳解
AVX-512(フォーマット)詳解MITSUNARI Shigeo
 
条件分岐とcmovとmaxps
条件分岐とcmovとmaxps条件分岐とcmovとmaxps
条件分岐とcmovとmaxpsMITSUNARI Shigeo
 
ソーシャルゲームのためのデータベース設計
ソーシャルゲームのためのデータベース設計ソーシャルゲームのためのデータベース設計
ソーシャルゲームのためのデータベース設計Yoshinori Matsunobu
 
Constexpr 中3女子テクニック
Constexpr 中3女子テクニックConstexpr 中3女子テクニック
Constexpr 中3女子テクニックGenya Murakami
 
できる!並列・並行プログラミング
できる!並列・並行プログラミングできる!並列・並行プログラミング
できる!並列・並行プログラミングPreferred Networks
 
RSA暗号運用でやってはいけない n のこと #ssmjp
RSA暗号運用でやってはいけない n のこと #ssmjpRSA暗号運用でやってはいけない n のこと #ssmjp
RSA暗号運用でやってはいけない n のこと #ssmjpsonickun
 
20分くらいでわかった気分になれるC++20コルーチン
20分くらいでわかった気分になれるC++20コルーチン20分くらいでわかった気分になれるC++20コルーチン
20分くらいでわかった気分になれるC++20コルーチンyohhoy
 
Master Canary Forging: 新しいスタックカナリア回避手法の提案 by 小池 悠生 - CODE BLUE 2015
Master Canary Forging: 新しいスタックカナリア回避手法の提案 by 小池 悠生 - CODE BLUE 2015Master Canary Forging: 新しいスタックカナリア回避手法の提案 by 小池 悠生 - CODE BLUE 2015
Master Canary Forging: 新しいスタックカナリア回避手法の提案 by 小池 悠生 - CODE BLUE 2015CODE BLUE
 
最適化超入門
最適化超入門最適化超入門
最適化超入門Takami Sato
 
いまさら聞けないarmを使ったNEONの基礎と活用事例
いまさら聞けないarmを使ったNEONの基礎と活用事例いまさら聞けないarmを使ったNEONの基礎と活用事例
いまさら聞けないarmを使ったNEONの基礎と活用事例Fixstars Corporation
 
CEDEC 2018 最速のC#の書き方 - C#大統一理論へ向けて性能的課題を払拭する
CEDEC 2018 最速のC#の書き方 - C#大統一理論へ向けて性能的課題を払拭するCEDEC 2018 最速のC#の書き方 - C#大統一理論へ向けて性能的課題を払拭する
CEDEC 2018 最速のC#の書き方 - C#大統一理論へ向けて性能的課題を払拭するYoshifumi Kawai
 
マルチコアを用いた画像処理
マルチコアを用いた画像処理マルチコアを用いた画像処理
マルチコアを用いた画像処理Norishige Fukushima
 
目grep入門 +解説
目grep入門 +解説目grep入門 +解説
目grep入門 +解説murachue
 
Deflate
DeflateDeflate
Deflate7shi
 

What's hot (20)

何となく勉強した気分になれるパーサ入門
何となく勉強した気分になれるパーサ入門何となく勉強した気分になれるパーサ入門
何となく勉強した気分になれるパーサ入門
 
明日使えないすごいビット演算
明日使えないすごいビット演算明日使えないすごいビット演算
明日使えないすごいビット演算
 
多段階計算の型システムの基礎
多段階計算の型システムの基礎多段階計算の型システムの基礎
多段階計算の型システムの基礎
 
いまさら聞けない!CUDA高速化入門
いまさら聞けない!CUDA高速化入門いまさら聞けない!CUDA高速化入門
いまさら聞けない!CUDA高速化入門
 
きつねさんでもわかるLlvm読書会 第2回
きつねさんでもわかるLlvm読書会 第2回きつねさんでもわかるLlvm読書会 第2回
きつねさんでもわかるLlvm読書会 第2回
 
計算機アーキテクチャを考慮した高能率画像処理プログラミング
計算機アーキテクチャを考慮した高能率画像処理プログラミング計算機アーキテクチャを考慮した高能率画像処理プログラミング
計算機アーキテクチャを考慮した高能率画像処理プログラミング
 
AVX-512(フォーマット)詳解
AVX-512(フォーマット)詳解AVX-512(フォーマット)詳解
AVX-512(フォーマット)詳解
 
条件分岐とcmovとmaxps
条件分岐とcmovとmaxps条件分岐とcmovとmaxps
条件分岐とcmovとmaxps
 
ソーシャルゲームのためのデータベース設計
ソーシャルゲームのためのデータベース設計ソーシャルゲームのためのデータベース設計
ソーシャルゲームのためのデータベース設計
 
Constexpr 中3女子テクニック
Constexpr 中3女子テクニックConstexpr 中3女子テクニック
Constexpr 中3女子テクニック
 
できる!並列・並行プログラミング
できる!並列・並行プログラミングできる!並列・並行プログラミング
できる!並列・並行プログラミング
 
RSA暗号運用でやってはいけない n のこと #ssmjp
RSA暗号運用でやってはいけない n のこと #ssmjpRSA暗号運用でやってはいけない n のこと #ssmjp
RSA暗号運用でやってはいけない n のこと #ssmjp
 
20分くらいでわかった気分になれるC++20コルーチン
20分くらいでわかった気分になれるC++20コルーチン20分くらいでわかった気分になれるC++20コルーチン
20分くらいでわかった気分になれるC++20コルーチン
 
Master Canary Forging: 新しいスタックカナリア回避手法の提案 by 小池 悠生 - CODE BLUE 2015
Master Canary Forging: 新しいスタックカナリア回避手法の提案 by 小池 悠生 - CODE BLUE 2015Master Canary Forging: 新しいスタックカナリア回避手法の提案 by 小池 悠生 - CODE BLUE 2015
Master Canary Forging: 新しいスタックカナリア回避手法の提案 by 小池 悠生 - CODE BLUE 2015
 
最適化超入門
最適化超入門最適化超入門
最適化超入門
 
いまさら聞けないarmを使ったNEONの基礎と活用事例
いまさら聞けないarmを使ったNEONの基礎と活用事例いまさら聞けないarmを使ったNEONの基礎と活用事例
いまさら聞けないarmを使ったNEONの基礎と活用事例
 
CEDEC 2018 最速のC#の書き方 - C#大統一理論へ向けて性能的課題を払拭する
CEDEC 2018 最速のC#の書き方 - C#大統一理論へ向けて性能的課題を払拭するCEDEC 2018 最速のC#の書き方 - C#大統一理論へ向けて性能的課題を払拭する
CEDEC 2018 最速のC#の書き方 - C#大統一理論へ向けて性能的課題を払拭する
 
マルチコアを用いた画像処理
マルチコアを用いた画像処理マルチコアを用いた画像処理
マルチコアを用いた画像処理
 
目grep入門 +解説
目grep入門 +解説目grep入門 +解説
目grep入門 +解説
 
Deflate
DeflateDeflate
Deflate
 

Similar to SSE4.2の文字列処理命令の紹介

Haswellサーベイと有限体クラスの紹介
Haswellサーベイと有限体クラスの紹介Haswellサーベイと有限体クラスの紹介
Haswellサーベイと有限体クラスの紹介MITSUNARI Shigeo
 
kagamicomput201707
kagamicomput201707kagamicomput201707
kagamicomput201707swkagami
 
Intro to SVE 富岳のA64FXを触ってみた
Intro to SVE 富岳のA64FXを触ってみたIntro to SVE 富岳のA64FXを触ってみた
Intro to SVE 富岳のA64FXを触ってみたMITSUNARI Shigeo
 
Summary of "Hacking", 0x351-0x354
Summary of "Hacking", 0x351-0x354Summary of "Hacking", 0x351-0x354
Summary of "Hacking", 0x351-0x354Taku Miyakawa
 
[TL06] 日本の第一人者が C# の現状と今後を徹底解説! 「この素晴らしい C# に祝福を!」
[TL06] 日本の第一人者が C# の現状と今後を徹底解説! 「この素晴らしい C# に祝福を!」[TL06] 日本の第一人者が C# の現状と今後を徹底解説! 「この素晴らしい C# に祝福を!」
[TL06] 日本の第一人者が C# の現状と今後を徹底解説! 「この素晴らしい C# に祝福を!」de:code 2017
 
kagami_comput2016_07
kagami_comput2016_07kagami_comput2016_07
kagami_comput2016_07swkagami
 
String representation in py3k
String representation in py3kString representation in py3k
String representation in py3kAtsuo Ishimoto
 
Node.jsでつくるNode.js ミニインタープリター&コンパイラー
Node.jsでつくるNode.js ミニインタープリター&コンパイラーNode.jsでつくるNode.js ミニインタープリター&コンパイラー
Node.jsでつくるNode.js ミニインタープリター&コンパイラーmganeko
 
kagami_comput2015_7
kagami_comput2015_7kagami_comput2015_7
kagami_comput2015_7swkagami
 
[Basic 13] 型推論 / 最適化とコード出力
[Basic 13] 型推論 / 最適化とコード出力[Basic 13] 型推論 / 最適化とコード出力
[Basic 13] 型推論 / 最適化とコード出力Yuto Takei
 
Lisp Tutorial for Pythonista : Day 3
Lisp Tutorial for Pythonista : Day 3Lisp Tutorial for Pythonista : Day 3
Lisp Tutorial for Pythonista : Day 3Ransui Iso
 
.NET Core 2.x 時代の C#
.NET Core 2.x 時代の C#.NET Core 2.x 時代の C#
.NET Core 2.x 時代の C#信之 岩永
 

Similar to SSE4.2の文字列処理命令の紹介 (20)

Prosym2012
Prosym2012Prosym2012
Prosym2012
 
Haswellサーベイと有限体クラスの紹介
Haswellサーベイと有限体クラスの紹介Haswellサーベイと有限体クラスの紹介
Haswellサーベイと有限体クラスの紹介
 
HPC Phys-20201203
HPC Phys-20201203HPC Phys-20201203
HPC Phys-20201203
 
Fbx解説 (1 構文編) (1)
Fbx解説 (1  構文編) (1)Fbx解説 (1  構文編) (1)
Fbx解説 (1 構文編) (1)
 
フラグを愛でる
フラグを愛でるフラグを愛でる
フラグを愛でる
 
kagamicomput201707
kagamicomput201707kagamicomput201707
kagamicomput201707
 
From IA-32 to avx-512
From IA-32 to avx-512From IA-32 to avx-512
From IA-32 to avx-512
 
Intro to SVE 富岳のA64FXを触ってみた
Intro to SVE 富岳のA64FXを触ってみたIntro to SVE 富岳のA64FXを触ってみた
Intro to SVE 富岳のA64FXを触ってみた
 
Summary of "Hacking", 0x351-0x354
Summary of "Hacking", 0x351-0x354Summary of "Hacking", 0x351-0x354
Summary of "Hacking", 0x351-0x354
 
[TL06] 日本の第一人者が C# の現状と今後を徹底解説! 「この素晴らしい C# に祝福を!」
[TL06] 日本の第一人者が C# の現状と今後を徹底解説! 「この素晴らしい C# に祝福を!」[TL06] 日本の第一人者が C# の現状と今後を徹底解説! 「この素晴らしい C# に祝福を!」
[TL06] 日本の第一人者が C# の現状と今後を徹底解説! 「この素晴らしい C# に祝福を!」
 
kagami_comput2016_07
kagami_comput2016_07kagami_comput2016_07
kagami_comput2016_07
 
Sml#探検隊
Sml#探検隊Sml#探検隊
Sml#探検隊
 
String representation in py3k
String representation in py3kString representation in py3k
String representation in py3k
 
Node.jsでつくるNode.js ミニインタープリター&コンパイラー
Node.jsでつくるNode.js ミニインタープリター&コンパイラーNode.jsでつくるNode.js ミニインタープリター&コンパイラー
Node.jsでつくるNode.js ミニインタープリター&コンパイラー
 
Scapy presentation
Scapy presentationScapy presentation
Scapy presentation
 
kagami_comput2015_7
kagami_comput2015_7kagami_comput2015_7
kagami_comput2015_7
 
[Basic 13] 型推論 / 最適化とコード出力
[Basic 13] 型推論 / 最適化とコード出力[Basic 13] 型推論 / 最適化とコード出力
[Basic 13] 型推論 / 最適化とコード出力
 
Lisp Tutorial for Pythonista : Day 3
Lisp Tutorial for Pythonista : Day 3Lisp Tutorial for Pythonista : Day 3
Lisp Tutorial for Pythonista : Day 3
 
PFI Seminar 2010/02/18
PFI Seminar 2010/02/18PFI Seminar 2010/02/18
PFI Seminar 2010/02/18
 
.NET Core 2.x 時代の C#
.NET Core 2.x 時代の C#.NET Core 2.x 時代の C#
.NET Core 2.x 時代の C#
 

More from MITSUNARI Shigeo

暗号技術の実装と数学
暗号技術の実装と数学暗号技術の実装と数学
暗号技術の実装と数学MITSUNARI Shigeo
 
範囲証明つき準同型暗号とその対話的プロトコル
範囲証明つき準同型暗号とその対話的プロトコル範囲証明つき準同型暗号とその対話的プロトコル
範囲証明つき準同型暗号とその対話的プロトコルMITSUNARI Shigeo
 
暗認本読書会13 advanced
暗認本読書会13 advanced暗認本読書会13 advanced
暗認本読書会13 advancedMITSUNARI Shigeo
 
Intel AVX-512/富岳SVE用SIMDコード生成ライブラリsimdgen
Intel AVX-512/富岳SVE用SIMDコード生成ライブラリsimdgenIntel AVX-512/富岳SVE用SIMDコード生成ライブラリsimdgen
Intel AVX-512/富岳SVE用SIMDコード生成ライブラリsimdgenMITSUNARI Shigeo
 
深層学習フレームワークにおけるIntel CPU/富岳向け最適化法
深層学習フレームワークにおけるIntel CPU/富岳向け最適化法深層学習フレームワークにおけるIntel CPU/富岳向け最適化法
深層学習フレームワークにおけるIntel CPU/富岳向け最適化法MITSUNARI Shigeo
 
WebAssembly向け多倍長演算の実装
WebAssembly向け多倍長演算の実装WebAssembly向け多倍長演算の実装
WebAssembly向け多倍長演算の実装MITSUNARI Shigeo
 
Lifted-ElGamal暗号を用いた任意関数演算の二者間秘密計算プロトコルのmaliciousモデルにおける効率化
Lifted-ElGamal暗号を用いた任意関数演算の二者間秘密計算プロトコルのmaliciousモデルにおける効率化Lifted-ElGamal暗号を用いた任意関数演算の二者間秘密計算プロトコルのmaliciousモデルにおける効率化
Lifted-ElGamal暗号を用いた任意関数演算の二者間秘密計算プロトコルのmaliciousモデルにおける効率化MITSUNARI Shigeo
 
BLS署名の実装とその応用
BLS署名の実装とその応用BLS署名の実装とその応用
BLS署名の実装とその応用MITSUNARI Shigeo
 
LazyFP vulnerabilityの紹介
LazyFP vulnerabilityの紹介LazyFP vulnerabilityの紹介
LazyFP vulnerabilityの紹介MITSUNARI Shigeo
 

More from MITSUNARI Shigeo (20)

暗号技術の実装と数学
暗号技術の実装と数学暗号技術の実装と数学
暗号技術の実装と数学
 
範囲証明つき準同型暗号とその対話的プロトコル
範囲証明つき準同型暗号とその対話的プロトコル範囲証明つき準同型暗号とその対話的プロトコル
範囲証明つき準同型暗号とその対話的プロトコル
 
暗認本読書会13 advanced
暗認本読書会13 advanced暗認本読書会13 advanced
暗認本読書会13 advanced
 
暗認本読書会12
暗認本読書会12暗認本読書会12
暗認本読書会12
 
暗認本読書会11
暗認本読書会11暗認本読書会11
暗認本読書会11
 
暗認本読書会10
暗認本読書会10暗認本読書会10
暗認本読書会10
 
暗認本読書会9
暗認本読書会9暗認本読書会9
暗認本読書会9
 
Intel AVX-512/富岳SVE用SIMDコード生成ライブラリsimdgen
Intel AVX-512/富岳SVE用SIMDコード生成ライブラリsimdgenIntel AVX-512/富岳SVE用SIMDコード生成ライブラリsimdgen
Intel AVX-512/富岳SVE用SIMDコード生成ライブラリsimdgen
 
暗認本読書会8
暗認本読書会8暗認本読書会8
暗認本読書会8
 
暗認本読書会7
暗認本読書会7暗認本読書会7
暗認本読書会7
 
暗認本読書会6
暗認本読書会6暗認本読書会6
暗認本読書会6
 
暗認本読書会5
暗認本読書会5暗認本読書会5
暗認本読書会5
 
暗認本読書会4
暗認本読書会4暗認本読書会4
暗認本読書会4
 
深層学習フレームワークにおけるIntel CPU/富岳向け最適化法
深層学習フレームワークにおけるIntel CPU/富岳向け最適化法深層学習フレームワークにおけるIntel CPU/富岳向け最適化法
深層学習フレームワークにおけるIntel CPU/富岳向け最適化法
 
私とOSSの25年
私とOSSの25年私とOSSの25年
私とOSSの25年
 
WebAssembly向け多倍長演算の実装
WebAssembly向け多倍長演算の実装WebAssembly向け多倍長演算の実装
WebAssembly向け多倍長演算の実装
 
Lifted-ElGamal暗号を用いた任意関数演算の二者間秘密計算プロトコルのmaliciousモデルにおける効率化
Lifted-ElGamal暗号を用いた任意関数演算の二者間秘密計算プロトコルのmaliciousモデルにおける効率化Lifted-ElGamal暗号を用いた任意関数演算の二者間秘密計算プロトコルのmaliciousモデルにおける効率化
Lifted-ElGamal暗号を用いた任意関数演算の二者間秘密計算プロトコルのmaliciousモデルにおける効率化
 
楕円曲線と暗号
楕円曲線と暗号楕円曲線と暗号
楕円曲線と暗号
 
BLS署名の実装とその応用
BLS署名の実装とその応用BLS署名の実装とその応用
BLS署名の実装とその応用
 
LazyFP vulnerabilityの紹介
LazyFP vulnerabilityの紹介LazyFP vulnerabilityの紹介
LazyFP vulnerabilityの紹介
 

Recently uploaded

デジタル・フォレンジックの最新動向(2024年4月27日情洛会総会特別講演スライド)
デジタル・フォレンジックの最新動向(2024年4月27日情洛会総会特別講演スライド)デジタル・フォレンジックの最新動向(2024年4月27日情洛会総会特別講演スライド)
デジタル・フォレンジックの最新動向(2024年4月27日情洛会総会特別講演スライド)UEHARA, Tetsutaro
 
AWS の OpenShift サービス (ROSA) を使った OpenShift Virtualizationの始め方.pdf
AWS の OpenShift サービス (ROSA) を使った OpenShift Virtualizationの始め方.pdfAWS の OpenShift サービス (ROSA) を使った OpenShift Virtualizationの始め方.pdf
AWS の OpenShift サービス (ROSA) を使った OpenShift Virtualizationの始め方.pdfFumieNakayama
 
モーダル間の変換後の一致性とジャンル表を用いた解釈可能性の考察 ~Text-to-MusicとText-To-ImageかつImage-to-Music...
モーダル間の変換後の一致性とジャンル表を用いた解釈可能性の考察  ~Text-to-MusicとText-To-ImageかつImage-to-Music...モーダル間の変換後の一致性とジャンル表を用いた解釈可能性の考察  ~Text-to-MusicとText-To-ImageかつImage-to-Music...
モーダル間の変換後の一致性とジャンル表を用いた解釈可能性の考察 ~Text-to-MusicとText-To-ImageかつImage-to-Music...博三 太田
 
CTO, VPoE, テックリードなどリーダーポジションに登用したくなるのはどんな人材か?
CTO, VPoE, テックリードなどリーダーポジションに登用したくなるのはどんな人材か?CTO, VPoE, テックリードなどリーダーポジションに登用したくなるのはどんな人材か?
CTO, VPoE, テックリードなどリーダーポジションに登用したくなるのはどんな人材か?akihisamiyanaga1
 
自分史上一番早い2024振り返り〜コロナ後、仕事は通常ペースに戻ったか〜 by IoT fullstack engineer
自分史上一番早い2024振り返り〜コロナ後、仕事は通常ペースに戻ったか〜 by IoT fullstack engineer自分史上一番早い2024振り返り〜コロナ後、仕事は通常ペースに戻ったか〜 by IoT fullstack engineer
自分史上一番早い2024振り返り〜コロナ後、仕事は通常ペースに戻ったか〜 by IoT fullstack engineerYuki Kikuchi
 
業務で生成AIを活用したい人のための生成AI入門講座(社外公開版:キンドリルジャパン社内勉強会:2024年4月発表)
業務で生成AIを活用したい人のための生成AI入門講座(社外公開版:キンドリルジャパン社内勉強会:2024年4月発表)業務で生成AIを活用したい人のための生成AI入門講座(社外公開版:キンドリルジャパン社内勉強会:2024年4月発表)
業務で生成AIを活用したい人のための生成AI入門講座(社外公開版:キンドリルジャパン社内勉強会:2024年4月発表)Hiroshi Tomioka
 
クラウドネイティブなサーバー仮想化基盤 - OpenShift Virtualization.pdf
クラウドネイティブなサーバー仮想化基盤 - OpenShift Virtualization.pdfクラウドネイティブなサーバー仮想化基盤 - OpenShift Virtualization.pdf
クラウドネイティブなサーバー仮想化基盤 - OpenShift Virtualization.pdfFumieNakayama
 
NewSQLの可用性構成パターン(OCHaCafe Season 8 #4 発表資料)
NewSQLの可用性構成パターン(OCHaCafe Season 8 #4 発表資料)NewSQLの可用性構成パターン(OCHaCafe Season 8 #4 発表資料)
NewSQLの可用性構成パターン(OCHaCafe Season 8 #4 発表資料)NTT DATA Technology & Innovation
 

Recently uploaded (8)

デジタル・フォレンジックの最新動向(2024年4月27日情洛会総会特別講演スライド)
デジタル・フォレンジックの最新動向(2024年4月27日情洛会総会特別講演スライド)デジタル・フォレンジックの最新動向(2024年4月27日情洛会総会特別講演スライド)
デジタル・フォレンジックの最新動向(2024年4月27日情洛会総会特別講演スライド)
 
AWS の OpenShift サービス (ROSA) を使った OpenShift Virtualizationの始め方.pdf
AWS の OpenShift サービス (ROSA) を使った OpenShift Virtualizationの始め方.pdfAWS の OpenShift サービス (ROSA) を使った OpenShift Virtualizationの始め方.pdf
AWS の OpenShift サービス (ROSA) を使った OpenShift Virtualizationの始め方.pdf
 
モーダル間の変換後の一致性とジャンル表を用いた解釈可能性の考察 ~Text-to-MusicとText-To-ImageかつImage-to-Music...
モーダル間の変換後の一致性とジャンル表を用いた解釈可能性の考察  ~Text-to-MusicとText-To-ImageかつImage-to-Music...モーダル間の変換後の一致性とジャンル表を用いた解釈可能性の考察  ~Text-to-MusicとText-To-ImageかつImage-to-Music...
モーダル間の変換後の一致性とジャンル表を用いた解釈可能性の考察 ~Text-to-MusicとText-To-ImageかつImage-to-Music...
 
CTO, VPoE, テックリードなどリーダーポジションに登用したくなるのはどんな人材か?
CTO, VPoE, テックリードなどリーダーポジションに登用したくなるのはどんな人材か?CTO, VPoE, テックリードなどリーダーポジションに登用したくなるのはどんな人材か?
CTO, VPoE, テックリードなどリーダーポジションに登用したくなるのはどんな人材か?
 
自分史上一番早い2024振り返り〜コロナ後、仕事は通常ペースに戻ったか〜 by IoT fullstack engineer
自分史上一番早い2024振り返り〜コロナ後、仕事は通常ペースに戻ったか〜 by IoT fullstack engineer自分史上一番早い2024振り返り〜コロナ後、仕事は通常ペースに戻ったか〜 by IoT fullstack engineer
自分史上一番早い2024振り返り〜コロナ後、仕事は通常ペースに戻ったか〜 by IoT fullstack engineer
 
業務で生成AIを活用したい人のための生成AI入門講座(社外公開版:キンドリルジャパン社内勉強会:2024年4月発表)
業務で生成AIを活用したい人のための生成AI入門講座(社外公開版:キンドリルジャパン社内勉強会:2024年4月発表)業務で生成AIを活用したい人のための生成AI入門講座(社外公開版:キンドリルジャパン社内勉強会:2024年4月発表)
業務で生成AIを活用したい人のための生成AI入門講座(社外公開版:キンドリルジャパン社内勉強会:2024年4月発表)
 
クラウドネイティブなサーバー仮想化基盤 - OpenShift Virtualization.pdf
クラウドネイティブなサーバー仮想化基盤 - OpenShift Virtualization.pdfクラウドネイティブなサーバー仮想化基盤 - OpenShift Virtualization.pdf
クラウドネイティブなサーバー仮想化基盤 - OpenShift Virtualization.pdf
 
NewSQLの可用性構成パターン(OCHaCafe Season 8 #4 発表資料)
NewSQLの可用性構成パターン(OCHaCafe Season 8 #4 発表資料)NewSQLの可用性構成パターン(OCHaCafe Season 8 #4 発表資料)
NewSQLの可用性構成パターン(OCHaCafe Season 8 #4 発表資料)
 

SSE4.2の文字列処理命令の紹介

  • 1. SSE4.2の文字列処理命令の紹介 Cybozu Labs 2011/8/6 光成滋生(8/23加筆修正p3, p.25) x86/x64最適化勉強会#1 2011/8/6 /41 1
  • 3. 説明とコード Intelプロセッサ最適化マニュアルを読もう http://homepage1.nifty.com/herumi/prog/intel-opt.html コード片 https://github.com/herumi/opti/ アライメントとページ境界に関する補足(必読) http://homepage1.nifty.com/herumi/diary/1108.html#8 2011/8/6 3 /41
  • 4. SIMD向き/不向き SIMDは4個(or 8/16)個のデータを同時に同じよう に処理するためのもの SIMD向き 先程の最大値を求める場合,4個ずつやってもよかった 複数個同時に加算,掛け算,etc. これらの処理は概ね得意 SIMD向きでないもの 本質的に分岐が多いもの 一つ前の状態に依存するもの 2011/8/6 4 /41
  • 5. SSE2によるstrlen 素朴なstrlen size_t strlenC(const char *p) { size_t len = 0; while (p[len]) len++; return len; } SIMDを使うアイデア 16byteずつデータを取得してbyte単位で0と比較する 0があれば0xff, なければ0をbyte単位に取得できる 上記データの最上位ビット(MSB)をかき集める 下から数えて初めて1になった場所があればそこが'¥0' 2011/8/6 5 /41
  • 6. SIMDを使うアイデア  xm0に文字列, xm1に0を入れておいてbyte比較  pcmpeqb xm0, xm1 xm0 +0 +1 +2 +3 +4 +5 +6 +7 xm0 h e l l o '¥0' ? ? xm1 0 0 0 0 0 0 0 0 xm0 0 0 0 0 0 0xff 0 0  xm0のMSBをかき集める  pmovmskb eax, xm0  1bitずつ16個合計16bitのデータになる  eax = 0b00100000  eaxが0で無ければ下から初めて1になる場所を探す  bsf eax, eax ; eax = 5となる 2011/8/6 6 /41
  • 7. 実装 https://github.com/herumi/opti/のstrlen_sse2.cpp 実際には16byteアライメントされていないと扱いにくいた めループ開始前に端数処理がある 当時3年前(gcc 4.3やVC9に対して)は速かった 最近のgcc(4.4以降?)は同様の手法やこれから述べる SSE4.2の命令が用いられている ただしgcc 4.6でもstrlenCがそういうコードになるわけではない  あくまでもライブラリ関数で使われているだけ まとめ アライメント処理 / byte単位での照合 / ビットスキャン 2011/8/6 7 /41
  • 8. SSE4.2の文字列処理命令 超複雑 今述べたアライメントの処理(不要になった), byte単位での照合(より高機能), ビットスキャンを1命令で行う pcmpXstrY xm0, xm1/mem, imm8 imm8でさまざまなモードを指定する memは16byte alignmentされていなくてもよい 0終端による文字列 edx/eaxによる長さ指定文字列 処理の結果をスキャンして pcmpistri pcmpestri ecxに出力 処理の結果をそのままxm0に pcmpistrm pcmpestrm 出力 2011/8/6 8 /41
  • 9. 長さ指定モード pcmpestri, pcmpestrmは明示的な長さを指定する pcmpestrY xm0, xm1, imm8 eaxはxm0の文字列の長さ edxはxm1の文字列の長さ 2011/8/6 9 /41
  • 10. imm8の内容 imm8[0](最下位ビット) 0なら入力データをbyte単位とみなす 1ならword(2byte)単位と見なして処理 imm8[1] 0なら符号無しデータ 1なら符号ありデータとして処理 imm8[3:2] 文字列の比較方法を選択する 00b(equal array) マッチする最初の文字を探す 01b(ranges) 範囲内から文字列を探す 10b(equal each) 文字列比較をする 11b(equal ordered) 部分文字列比較をする 2011/8/6 10 /41
  • 11. equal anyの疑似コード set : 検索したい文字集合 text : 検索対象のテキスト for (int j = 0; j < len; j++) { int tmp = 0; for (int i = 0; i < len; i++) { tmp |= text[j] == set[i]; } IntRes1[j] = tmp; } set = "abc", text = "xaybzc"; IntRes1[0] = setのどれもtext[0]にマッチしないのでfalse IntRes1[1] = setのaがtext[1]にマッチするのでtrue ... 2011/8/6 11 /41
  • 12. rangesの疑似コード str:[2*i+0] : 文字列範囲の始め str[2*i+1] : 文字列範囲の終わり for (int j = 0; j < len; j++) { int tmp = 0; for (int i = 0; i < len; i += 2) { tmp |= (str[i] <= text[j]) && (text[j] <= str[i + 1]); } IntRes1[j] = tmp; } str = "az09", text = "0Abc"; IntRes1[0] = text[0]は[0-9]にあるのでtrue IntRes1[1] = text[1]はどこにも入らないのでfalse IntRes1[2] = text[2]は[a-z]にあるのでtrue 2011/8/6 12 /41
  • 13. equal eachの疑似コード str : 検索したい文字列 text : 検索対象のテキスト for (int j = 0; j < len; j++) { IntRes1[j] = text[j] == str[j]; } str = "abc", text = "aXc"; IntRes1[0] = text[0] == aなのでtrue IntRes1[1] = text[1] != bなのでfalse IntRes1[2] = text[2] == cなのでtrue 2011/8/6 13 /41
  • 14. equal orderedの疑似コード str : 検索したい文字列 text : 検索対象のテキスト for (int j = 0; j < len; j++) { int tmp = 1; for (int i = 0; i < len - j; i++) { tmp &= text[j + i] == str[i]; } IntRes1[j] = tmp; } これは複雑なのでinvalid文字列の扱いを説明して から 2011/8/6 14 /41
  • 15. imm8[5:4] 中間結果(IntRes1)に作用してIntRes2を作る 00b(positive polarity) そのまま出力(IntRes2[i] = IntRes1[i]) 01b(negative polarity) 反転して出力(IntRes2[i] = ~IntRes1[i]) 10b(masked(+)) そのまま 01b(masked(-)) 入力がinvalidならそのまま,そうでなければ反転 2011/8/6 15 /41
  • 16. imm8[6] 最終結果操作 IntRes2からecxかxm0に出力するデータを作る pcmpestri, pcmpistriに対する設定 0ならIntRes2のLSBが入る 1ならIntRes2のMSBが入る ただしIntRes2 == 0なら16(byteのとき)か8(wordのとき) pcmpestrm, pcmpistrmに対する設定 0ならIntRes2が0拡張されてxmm0に入る. 1ならIntRes2の各ビットが入力データの型の大きさにした がってbyteかword単位のマスクに拡張されてxmm0に入 る. 2011/8/6 16 /41
  • 17. invalid文字の扱い invalid文字 文字列の比較において,途中に'¥0'が出たり,長さが短く て終わってしまったときの残りの文字たちのこと invalid文字と通常の文字との演算ルール xm1(str) xm2(text) equal any ranges equal each equal orderd valid valid 通常通り 通常通り 通常通り 通常通り valid invalid 常に0 常に0 常に0 常に0 invalid valid 常に0 常に0 常に0 常に1 invalid invalid 常に0 常に0 常に1 常に1 2011/8/6 17 /41
  • 18. equal orderedの例 src = "ABCA¥0XYZ", text = "BABCAB¥0S"; text文字列 7 6 5 4 3 2 1 0(j) S ¥0 B A C B A B src 0(i) A fF fF F T F F T F F 文 1 B fF fF T F F T F x T 字 F 列 2 C fF fF F F T F x x F 3 A fF fF F T F x x x F F 4 ¥0 fT fT fT fT fT x x x F 5 X fT fT fT x x x x x F 6 Y fT fT x x x x x x IntRes1 7 Z fT x x x x x x x '¥0'の後ろはinvalid http://journal.mycom.co.jp/articles/2008/04/10/idf09/008.html T:True, F:False, fT:force True, fF:force Flase 2011/8/6 18 /41
  • 19. フラグレジスタ フラグレジスタは次のように変化する pcmpXstrY xm0, xm1, imm8 pcmpestri / pcmpestrm pcmpistri / pcmpistrm CF IntRes2 != 0 ZF |edx| < 16 xm1のいずれかが0なら1, そうでなければ0 SF |eax| < 16 xm0のいずれかが0なら1, そうでなければ0 OF IntRes2[0] 2011/8/6 19 /41
  • 20. SSE4.2を使ったstrlen pcmpistriを選択 0終端文字列を扱い,位置をビットスキャンで取る imm8[1:0] = 0 符号無しbyte単位 '¥0'を探す 1~255にマッチしないものを見つけると考える 範囲指定なのでrangesを使う(imm8[3:2] = 01b) 中間結果操作でビット反転をするのでimm8[5:4]=01b 最終結果の下から初めての1を見つけるのでimm8[6]=0 よってimm8=0b010100=0x14となる 16byteの中に'¥0'がなければZF = 0 2011/8/6 20 /41
  • 21. SSE4.2を使ったstrlen よってコードは次のようになる とても簡単 mov(eax, 0xff01); // { '0x01', '0xff', '¥0' }; movd(xm0, eax); // xm0にrangesの文字列を設定 mov(a, p); // 文字列の先頭 jmp(".in"); L("@@"); add(a, 16); L(".in"); pcmpistri(xm0, ptr [a], 0x14); // 比較して jnz("@b"); add(a, c); sub(a, p); ret(); 2011/8/6 21 /41
  • 22. ベンチマーク  ランダムな長さの文字列に対するbyteあたりの処理時間 文字列平均長 5.04 66.62 261.78 1063.83 glibc 5.70 0.58 0.27 0.20 strlenC 6.92 1.89 1.41 1.24 SSE2 5.74 0.54 0.21 0.15 SSE4.2 5.03 0.69 0.29 0.21  SSE2バージョンは文字列長が長いと速い  32byte単位で処理しているため  glibcとSSE4.2は大体同じ  短いところでglibcが若干速いのは短いとき用の処理が入ってる から 2011/8/6 22 /41
  • 23. 注意 初期のintelのマニュアルは遅いバージョンが載っていた http://journal.mycom.co.jp/articles/2008/04/10/idf09/008.html L("@@"); add(a, ecx); // 16でなくてecx L(".in"); pcmpistri(xm0, ptr [a], 0x14); // 比較して jnz("@b"); これではpcmpistriの出力結果のecxが確定するまでadd で値を計算できないためスループットが下がるため 2011/8/6 23 /41
  • 24. intrinsic版の注意点 pcmpistriなどの命令はecx(またはxm0)とフラグの 両方を出力するがintrinsicは一つしか値をとれない 個別の値を取る関数が用意されている 返り値 pcmpestri pcmpestrm pcmpistri pcmpistrm ecx/xmm0 _mm_cmpestri _mm_cmpestrm _mm_cmpistri _mm_cmpistrm CF = 0 && ZF = 0 _mm_cmpestra _mm_cmpistra CF _mm_cmpestrc _mm_cmpistrc OF _mm_cmpestro _mm_cmpistro SF _mm_cmpestrs _mm_cmpistrs ZF _mm_cmpestrz _mm_cmpistrz _mm_cmpestraなどはAFを取得するものではない AFは常に0に設定される またhttp://msirocoder.blog35.fc2.com/blog-entry-65.html で触れられているようなResetでも無いように思う(自信無し) 2011/8/6 24 /41
  • 25. SSE4.2 intrinsic版strlen #ifdef _WIN32 *(__m128i*)pはmovdqaが生成される可能性がある #include <intrin.h> _mm_loadu_si128((__m128i*)p)を使うべき #else (他のスライドも同様) cf. #include <x86intrin.h> http://homepage1.nifty.com/herumi/diary/1108.html#8 #endif size_t strlenSSE42_C(const char* top) { const __m128i im = _mm_set1_epi32(0xff01); const char *p = top - 16; do { p += 16; } while (!_mm_cmpistrz(im, *(__m128i*)p, 0x14)); // ZF p += _mm_cmpistri(im, *(__m128i*)p, 0x14); // get ecx return p - top; } msiroさんのwhile()の方が簡潔でよさそう asmコードと実行時間は同じようなものになる 2011/8/6 25 /41
  • 26. 生成コード(by gcc 4.6.0) lea rdx, [rdi-16] movdqa xmm0, XMMWORD PTR .LC0[rip] jmp .L11 // align16 .L12: mov rdx, rax .L11: lea rax, [rdx+16] pcmpistri xmm0, XMMWORD PTR [rdx+16], 20 jne .L12 _mm_cmpistrzと_mm_cmpistriの両方が一つの pcmpistriにまとめられている よかった.たいしたもんだ 2011/8/6 26 /41
  • 27. strchr by SSE4.2 ナイーブな実装 const char *strchr_C(const char *p, int c) { while (*p) { if (*p == (char)c) return p; p++; } return 0; } imm8の選択 符号無しbyte単位でimm8[1:0] = 0 集計はequal anyでimm8[3:2] = 0 文字列は"(char)c"; 中間結果と最終結果は何もしないimm8[6:5:4] = 0 2011/8/6 27 /41
  • 28. 何のフラグでループするか IntRes2 != 0なら文字を発見したのでループ脱出 CFを確認する 次に文字列が終了したかをZFで確認する #lp pcmpistri xm0, ptr [p], 0 jc #found jnz #lp CF = 0 && ZF = 0のとき,すなわちjaとすればよい #lp p += 16 pcmpistri xm0, ptr [p], 0 ja #lp jnc #notfound #found 2011/8/6 28 /41
  • 29. コードとベンチマーク const char *strchrSSE42_C(const char* p, int c) { const __m128i im = _mm_set1_epi32(c & 0xff); while (_mm_cmpistra(im, *(const __m128i*)p, 0)) { p += 16; } if (_mm_cmpistrc(im, *(const __m128i*)p, 0)) { return p + _mm_cmpistri(im, *(const __m128i*)p, 0); } return 0; } Xeon strchrLIB strchr_C strchrSSE42 clk 0.459 3.012 0.252 2011/8/6 29 /41
  • 30. 範囲指定への拡張 const char *findRange_C(const char* p, char c1, char c2) { const unsigned char *up = (const unsigned char *)p; unsigned char uc1 = c1; unsigned char uc2 = c2; while (*up) { if (uc1 <= *up && *up <= uc2) return (const char*)up; up++; } return 0; } if ((unsigned char)(*p - c1) <= (unsigned char)(c2 - c1)) return p; により1割ぐらい早くなるが SSE4.2版はequal anyをrangesにするだけ 2011/8/6 30 /41
  • 31. findRange by SSE4.2 const char *findRange_SSE42(const char* p, char c1, char c2) { const __m128i im = _mm_set1_epi32( ((unsigned char)c1) | (((unsigned char)c2) << 8) ); while (_mm_cmpistra(im, *(const __m128i*)p, 4)) { p += 16; } if (_mm_cmpistrc(im, *(const __m128i*)p, 4)) { return p + _mm_cmpistri(im, *(const __m128i*)p, 4); } return 0; } i7 findRange_C findRange2_C findRange_SSE42 clk 2.310 2.055 0.214 2011/8/6 31 /41
  • 32. 単語のカウント ここでは英数字とアポストロフィの連続を単語とし, その個数を数える(インテルのマニュアルより) インテルの比較用Cコードはかなりトリッキー だが,凄く速いというわけでもない(おもしろいけど) size_t countWord_C(const char *p) { static const char alp_map8[32] = { 0, 0, 0, 0, 0x80, 0, 0xff, 0x3, 0xfe, 0xff, 0xff, 0x7, 0xfe, 0xff, 0xff, 0x7 }; size_t i = 1, cnt = 0; unsigned char cc, cc2; bool flag[3]; cc2 = cc = p[0]; flag[1] = alp_map8[cc >> 3] & (1 << (cc & 7)); while (cc2) { cc2 = p[i]; flag[2] = alp_map8[cc2 >> 3] & (1 << (cc2 & 7)); if (!flag[2] && flag[1]) cnt++; flag[1] = flag[2]; i++; } return cnt; } 2011/8/6 32 /41
  • 33. 多少最適化したもの 今回はこれを使う(2倍程度速い) static char alnumTbl2[256]; // 単語になる文字だけ1, それ以外は0 size_t countWord_C2(const char *p){ size_t count = 0; unsigned char c = *p++; char prev = alnumTbl2[c]; while (c) { c = *p++; char cur = alnumTbl2[c]; if (!cur && prev) { count++; } prev = cur; } return count; } 2011/8/6 33 /41
  • 34. SSE4.2 intrinsc版countWord msiroさんのを少し変更 MIE_ALIGN(16) static const char alnumTbl[16] = { '¥'', '¥'', '0', '9', 'A', 'Z', 'a', 'z', '¥0' }; size_t countWord_SSE42(const char *p) { const __m128i im = *(const __m128i*)alnumTbl; __m128i ret, x, prev = _mm_setzero_si128(); size_t count = 0; goto SKIP; do { p += 16; SKIP: ret = _mm_cmpistrm(im, *(const __m128i*)p, 0x4); x = _mm_slli_epi16(ret, 1); x = _mm_or_si128(prev, x); prev = _mm_srli_epi32(ret, 15); x = _mm_xor_si128(x, ret); count += _mm_popcnt_u32(_mm_cvtsi128_si32(x)); } while (!_mm_cmpistrz(im, *(const __m128i*)p, 0x4)); return count / 2; } 2011/8/6 34 /41
  • 35. countWord_SSE42の解説 ', 0-9, A-Z, a-zの範囲にあるもの(C)を探す rangesを使う (C)とそれ以外の境界は0から1, 1から0に変化する エッジは1bitずらしてxorすれば検出できる 1 1 1 0 0 1 0 0 1 1 1 0 0 1 0 0 0 次回使う xor 0 0 1 0 1 1 0 0 ビットの立っている個数を数える 倍数えることになるので最後に2で割る 2011/8/6 35 /41
  • 36. SSE4.2 intrinsc版countWord (C)に入る範囲を配列で指定する MIE_ALIGN(16) static const char alnumTbl[16] = { '¥'', '¥'', '0', '9', 'A', 'Z', 'a', 'z', '¥0' }; const __m128i im = *(const __m128i*)alnumTbl; 符号無しbyte単位なのでimm8[1:0] = 0 集計方法はranges imm8[3:2] = 01b 中間操作:そのまま出力 imm8[5:4] = 0 最終操作:そのまま出力 imm8[6] = 0 よってimm8 = 0b100 = 4 2011/8/6 36 /41
  • 37. SSE4.2 intrinsc版countWord do { p += 16; ret = _mm_cmpistrm(im, *(const __m128i*)p, 0x4); x = _mm_slli_epi16(ret, 1); x = _mm_or_si128(prev, x); prev = _mm_srli_epi32(ret, 15); x = _mm_xor_si128(x, ret); count += _mm_popcnt_u32(_mm_cvtsi128_si32(x)); } while (!_mm_cmpistrz(im, *(const __m128i*)p, 0x4)); retの各ビットに(c in (C)) ? 1 : 0が入る x = ret << 1; x = x | prev; // 前回の残りの1bit prev = ret >> 15; x = x ^ ret. count += xのビットの数 文字列の中に'¥0'が見つかるまでループ 2011/8/6 37 /41
  • 38. ベンチマーク i7 インテルオリジナル 改良版 intrinsic版 clk 854 456 42  一桁速い! L("@@"); add(p, 16);  生成コードを見てみる movdqa(xm1, ptr [p]);  あれ,pcmpistrmが2回 pcmpistrm(xm2, xm1, 4); movdqa(xm4, xm0);  pcmpistrmはフラグを変える psllw(xm4, 1); por(xm4, xm3);  真ん中のadd(a, d);が邪魔 pxor(xm4, xm0);  lea(a, ptr [a + d]);にすればOK? movd(d, xm4); movdqa(xm3, xm0); popcnt(d, d); add(a, d); psrld(xm3, 15); pcmpistrm(xm2, xm1, 4); jnz("@b"); 2011/8/6 38 /41
  • 39. 改良  実はpopcntもフラグをいじる L("@@");  命令の順序を入れ換えてしまおう movdqa(xm4, xm0); psllw(xm4, 1); por(xm4, xm3); movdqa(xm3, xm0); pxor(xm4, xm0); psrld(xm3, 15); movd(d, xm4); popcnt(d, d); add(p, 16);  速くなった! add(a, d); pcmpistrm(xm2, ptr [p], 4); jnz("@b"); i7 インテルオリジナル 改良版 intrinsic版 改良版 clk 854 456 42 33 2011/8/6 39 /41
  • 40. 改悪?  Xeonでは遅くなっていた i7 インテルオリジナル 改良版 intrinsic版 改良版 clk 854 456 42 33 Xeon インテルオリジナル 改良版 intrinsic版 改良版? clk 1714 874 46 53  いろいろ難しい… 2011/8/6 40 /41
  • 41. まとめ SSE4.2の命令の紹介 アライメントを気にする必要がない 超高機能な文字マッチパターン bsf, bsr相当の機能も持つ intrinsic版はフラグとecx/xm0の両方必要 手動最適化の余地あり? プロセッサによって結構性能が違うこともある 2011/8/6 41 /41