SIMDで整数除算
  @Shobomaru
更新履歴

• 2012/11/01 v1.0
  Slideshareのテストも兼ねて試験的に公開




            (´・ω・`)Shobomaru   2
2011年12月下旬くらいの出来事


( ^o^) SIMDで遊ぼう!

( ˘⊖˘) 。o( 待てよ?浮動小数点数の除算はrcpss命令だけど、整数の除算は? )

| In●el |┗(☋` )┓三

( ◠‿◠ )☛ そこに気づいたか・・・消えてもらう

▂▅▇█▓▒░(’ω’)░▒▓█▇▅▂うわああああああ




                      (´・ω・`)Shobomaru    3
詰んだ?


• 詰んだ。終了。解散。




• …ただし、除数が固定かつ16bit以下なら
  まだ希望がある




           (´・ω・`)Shobomaru   4
おさらい・浮動小数点数の除算

• 直接除算する方法
 – __m128 _mm_div_ps( __m128 a, __m128 b );
    • DIVPS xmm1, xmm2 (SSE1)
    • 遅い代わりに精度が高い
       – スループットは1/20、遅すぎ

• 逆数を求めて乗算にする方法
 – __m128 _mm_rcp_ps( __m128 a );
    • RCPPS xmm1, xmm2 (SSE1)
    • 速い代わりに精度が低い
       – その後乗算も必要




                      (´・ω・`)Shobomaru        5
おさらい・逆数による除算から乗算への変換

• 例:“8.0÷5.0=1.6”
  – = 8.0×(1.0÷5.0)
  – = 8.0×0.2 // ←この“0.2”を求めるのが逆数命令
  – = 1.6




                    (´・ω・`)Shobomaru   6
整数の除算

• 除算命令
 – ない…
• 逆数命令
 – ない…
• 右シフト命令
 – 2の冪乗でしか使えない


• ⇒ なんとか自分で逆数を求めるしかない
 – でも逆数は1以下なので、整数では表現できない…


           (´・ω・`)Shobomaru   7
整数を固定小数として扱う

• 最上位ビット(MSB)が0.5、MSBの隣が0.25、
  その隣が0.125…
• 例:“0.2”
 – = 0.125 + 0.0625 + 0.00078125 + 0.000390625 + …
 – 2進数表現では(とりえず16bitで)
   “0.0011001100110011”
 – 小数点以下を固定小数として取り出す
   “13037”
 – 固定小数なので、2^16である“65536”が1を表す
    • 13037÷65536=0.199996948242188≒0.2



                     (´・ω・`)Shobomaru                8
固定小数の掛け算

• 例:“8÷5”
  – = 8×13037÷65536
    • この除算は16bit論理右シフトで代用可能
      “(8*13037) >> 16”
  – = 1.5999755859375
  – 整数でキャストして答えは“1”
         _,,-―=''' ̄           ___,,-―――='' ̄ __,-―='' ̄           /
              _,,-―=''' ̄                _,,-―='' ̄ ヽ               /     +
          ̄ ̄               _,,-―=''' ̄                   \        /    . . .     .
                    ,,-='' ̄       _ノ                 ,_ノ ヽ      /         .  。. ★     ☆
               ,,,-''             / iニ)ヽ,             /rj:ヽヽ ヽ/             。.       .
         -―'' ̄               ;〈 !:::::::c! |___,/' {.::::::;、! } |        -┼- 丿~~~| |~~~~~| __ ■
         .   |.                   (つ`''"     |      / `'ー''(つ. |.    -┼- /~~~~/      丿 | 丿 ▼ ▼
             | .            /////         |       /       /// |     |        丿    /     丿    ● ●
             ヽ       γ´~⌒ヽ.               |     /             /
         ――ヽ      /         ヽ            | /               /⌒ヽ、
                \/             |            |_/           /      ヽ



                              (´・ω・`)Shobomaru                                         9
精度の問題

• この方法で“10÷5”を計算すると…
 – 10×13037÷65536
 – = 1.99996948242188
 – 整数でキャストして“1”…???
        ,,-―=''' ̄            ___,,-―――='' ̄ __,-―='' ̄               /
              _,,-―=''' ̄                _,,-―='' ̄ ヽ                  /     +
         ̄ ̄                _,,-―=''' ̄                   \           /   . . .      .
                   ,,-='' ̄       _ノ               ,_ノ      ヽ       /         .   。. ★    ☆
              ,,,-''             / iニ)ヽ,            /rj:ヽヽ       ヽ/            。.       .
        -―'' ̄                ;〈 !:::::::c!          ' {.::::::;、! 〉      |      ̄ ̄| _|_       丿 |~~~~~|
        .   |                    (つ`''"      __ `'ー''(つ          |         |     | /|.        丿 | 丿
            |          /////               |   |         /// |       __|       丿      |    /    丿
           ヽ        γ´~⌒ヽ.               /      |              /
        ――ヽ      /           ヽ        /         |            /⌒ヽ、
               \/              |        | ̄ ̄ ̄ ̄|           /          ヽ


• なぜ?
 – 逆数の丸め誤差を考慮していないから
                              (´・ω・`)Shobomaru                                             10
Terje Mathisenのアルゴリズム(?) [1]

• 逆数の小数点の位置をできるだけ右にずらす
 – 代償として、乗算後の論理右シフトの量を調整する

    xを割られる数、dを割る数とするとき、
    b = (有効ビット数) – 1
    r=w+b
    f = 2r / d
    もしfが整数ならば、case Aへ
    もしfの小数部が0.5未満ならば、case Bへ
    もしfの小数部が0.5を超えるならば、case Cへ
    case A: result = x SHR b
    case B: result = ( ( x + 1 ) * f ) SHR r
               ただし、fは切り捨て
    case C: result = ( x * f ) SHR r
               ただし、fは切り上げ
                                    SHRは論理右シフトのこと

                    (´・ω・`)Shobomaru                11
C言語のプログラム(1)
                                             引数        意味

int short_rcp(                                 div        割る数
unsigned short div,                            rcp         逆数
unsigned short *rcp,
int *shift,                                   shift    論理右シフト量
unsigned short *bias )                        bias         補正
{
       int b = 0;                            戻り値     divが2の冪乗か否か
       for( int i = 0; i < 16; i++ ) {
               if( ( ( div >> ( 15 - i ) ) & 0x1 ) == 1 ) {
                       b = 15 - i;
                       break;
               }
       }

       unsigned int r = 16 + b;
       unsigned int r2 = 1 << r;
       double f = (double)r2 / div;
       double fm = fmod( f, 1.0 );


                          (´・ω・`)Shobomaru                   12
C言語のプログラム(2)
    if( fm == 0.0 )
    {
           *shift = b;
           *rcp = 1;
           *bias = 0;
           return 1;
    }
    else if( fm < 0.5 )
    {
           *shift = b;
           *rcp = (unsigned short)f;
           *bias = 1;
           return 0;
    }
    else
    {
           *shift = b;
           *rcp = (unsigned short)( f + 0.5 );
           *bias = 0;
           return 0;
    }
}                      (´・ω・`)Shobomaru          13
C言語のプログラム(3)
const unsigned   short dividend = 10;
const unsigned   short divisor = 5;
unsigned short   rcp;
int shift;
unsigned short   bias;

int ans;
int pow2 = short_rcp( divisor, &rcp, &shift, &bias );
if( pow2 ) ans = dividend >> shift;
else       ans = ( ( dividend + bias ) * rcp ) >> ( shift + 16 );

• ansは期待通り”2”
• 割られる数が32769以上のとき、不正な解を出す
   – C言語の整数拡張ルールの関係で、乗算が符号付きに
     なってしまう
   – 楽しいアセンブラプログラミングが待ち受ける

                            (´・ω・`)Shobomaru                    14
逆数求めるの面倒すぎじゃね?

• だから言っただろう、
  「ただし、除数が固定なら」と。
 – 面倒な計算も初回だけなら我慢できる


• 除数が固定なら、変数pow2も不変なので、
  条件分岐のコストは考えなくてよい
 – Branch Target Bufferのない糞CPUなんぞ知らん


• 整数拡張もSSEなら自分で操作できる
 – アセンブラいらない!

                (´・ω・`)Shobomaru       15
SSE2を使ったプログラム
__m128i   mdivident;
__m128i   mrcp = _mm_set1_epi16( rcp );
__m128i   mbias = _mm_set1_epi16( bias );
__m128i   mans;

mdivident = _mm_load_si128( [メモリアドレス] );
if( pow2 ) mans = _mm_srli_epi16( mdibident, shift );
else       mans = _mm_srli_epi16( _mm_mulhi_epu16(
       _mm_add_epi16( mdivident, mbias ), mrcp ), shift );

• _mm_mulhi_epi16 ()は乗算後の上位ビットを返す
   – 上位型への拡張は要らない
   – 【悲報】上位ビットを返す乗算は、符号つきorなしの
     16bit整数しかない
          • 8bitなら16bitにunpack、32bitは終了


                            (´・ω・`)Shobomaru                 16
割る数がUSHORT_MAXのときの問題


• biasが1、divisorが65535のとき、直後の加算で
  整数オーバーフローによって不正な解になる

• どうしようもないので、
  分岐してスカラで計算するか、
  型昇格するかで回避するしかない
 – できたらUSHORT_MAXが来ないようにする




             (´・ω・`)Shobomaru      17
実は賢いコンパイラ

• 実は、C言語で定数の除算式を書くと
  勝手に乗算+論理右シフトにしてくれる
      – 割る数が2の冪乗なら右シフトだけ

volatile unsigned int dividend = 8;
unsigned int ans = dividend / 5;

mov     ecx,   ***
mov     eax,   0CCCCCCDh
mul     eax,   ecx
shr     edx,   2

(Visual C++ 10.0 / Release)




                           (´・ω・`)Shobomaru   18
BSR命令を使った最適化(2)

• 実は、有効ビット数の計算はx86専用命令がある
• BSR命令
 – ただし、0(立っているビットがない)は未定義値
  • 除算なので、そもそも逆数に0が入ってくる時点でおかしい
     – assert()なりthrowなり自分で例外処理する
  • 0が未定義でないLZCNT命令もあるが、AMD専用(SSE4a)
 – イントリンシック命令
  • _BitScanReverse() (Visual C++) ※intrin.hをinclude
  • _bit_scan_reverse() (Intel C++ Compiler)
  • __builtin_clz() (GNU gcc)
     – ARMとかMIPSとかでも使える
     – xor 16を取る必要あり?(要確認)

                      (´・ω・`)Shobomaru                 19
BSR命令を使った最適化(2)
unsigned long bl;
_BitScanReverse( &bl, div );

//int b = 16;
//for( int i = 0; i < 16; i++ ) {
//     if( ( ( div >> ( 15 - i ) ) & 0x1 ) == 1 ) {
//             b = 15 - i;
//             break;
//     }
//}
int b = bl;

• といっても逆数を求める部分なので、
  効果はほとんどない



                          (´・ω・`)Shobomaru            20
_mm_set1_epi16()

• 実は複数の命令に変換されてしまう
  – mov + punpcklwd + pshufd


• SSSE3なら_mm_shuffle_pi8()、
  AVX1なら_mm_broadcastw_epi16()
  でpunpcklwdは不要になる
  – movは消せないけど、IvyBridgeからmovはリネームス
    テージで消滅するらしいから、多分気にしないでいい
    • AMD?なにそれおいしいの?




                    (´・ω・`)Shobomaru   21
ベンチマーク


• …良く考えたら整数除算する機会ってなくね?

• てなわけで飽きました(:P




           (´・ω・`)Shobomaru   22
テーブル参照


• 除数固定、入力値の範囲が大きくなければ、
  SSEの代わりに除算結果のテーブルを
  作ってしまうのもアリ
 – テーブル参照なので、SSEは使えない




           (´・ω・`)Shobomaru   23
問題点

• SSE/AVXの乗算は16/32bit型だけ
 – 8bit型はpack/unpackで16bitに変換する必要あり


• 符号つき整数は一工夫必要
 – 乗算を符号付き、シフトを算術シフトにすればいい?
   • 試すのマンドクセ(‘A`)




                (´・ω・`)Shobomaru      24
ARM NEONでは…

• NEONも整数の除算はない
• 乗算はある
 – VQDMULH命令
   • ただし、符号つき16/32bitのみ…
 – VMULL命令
   • 符号なし8/16/32bit、後で自分で上位ビットを取り出す
• なぜか整数の逆数命令もある
 – VRECPE/VRECPS命令
   • 符号なし32bit(と32bit小数)
 – 精度とかはよく知らない
   • Newton-Raphson法が必要?試すのマンドクセ(‘A`)

                 (´・ω・`)Shobomaru       25
まとめ


• SSEに整数の除算命令はない

• なんとか逆数を作ることで、
  乗算を使って除算の代用が可能

• それでも制約がいっぱい
 – 早く整数除算命令を作ってくれ



           (´・ω・`)Shobomaru   26
参考文献


1. Optimizing subroutines in assembly language
   http://www.agner.org/optimize/optimizing_assem
   bly.pdf

• というか、ほぼパクリです。すみません。




                   (´・ω・`)Shobomaru            27
ライセンス


• このスライドは全て、
  「クリエイティブ・コモンズ 表示 2.1」
  の下で提供しています
  (ただし引用した図・文字を除く)




           (´・ω・`)Shobomaru   28

SIMDで整数除算

  • 1.
  • 2.
    更新履歴 • 2012/11/01 v1.0 Slideshareのテストも兼ねて試験的に公開 (´・ω・`)Shobomaru 2
  • 3.
    2011年12月下旬くらいの出来事 ( ^o^) SIMDで遊ぼう! (˘⊖˘) 。o( 待てよ?浮動小数点数の除算はrcpss命令だけど、整数の除算は? ) | In●el |┗(☋` )┓三 ( ◠‿◠ )☛ そこに気づいたか・・・消えてもらう ▂▅▇█▓▒░(’ω’)░▒▓█▇▅▂うわああああああ (´・ω・`)Shobomaru 3
  • 4.
  • 5.
    おさらい・浮動小数点数の除算 • 直接除算する方法 –__m128 _mm_div_ps( __m128 a, __m128 b ); • DIVPS xmm1, xmm2 (SSE1) • 遅い代わりに精度が高い – スループットは1/20、遅すぎ • 逆数を求めて乗算にする方法 – __m128 _mm_rcp_ps( __m128 a ); • RCPPS xmm1, xmm2 (SSE1) • 速い代わりに精度が低い – その後乗算も必要 (´・ω・`)Shobomaru 5
  • 6.
    おさらい・逆数による除算から乗算への変換 • 例:“8.0÷5.0=1.6” – = 8.0×(1.0÷5.0) – = 8.0×0.2 // ←この“0.2”を求めるのが逆数命令 – = 1.6 (´・ω・`)Shobomaru 6
  • 7.
    整数の除算 • 除算命令 –ない… • 逆数命令 – ない… • 右シフト命令 – 2の冪乗でしか使えない • ⇒ なんとか自分で逆数を求めるしかない – でも逆数は1以下なので、整数では表現できない… (´・ω・`)Shobomaru 7
  • 8.
    整数を固定小数として扱う • 最上位ビット(MSB)が0.5、MSBの隣が0.25、 その隣が0.125… • 例:“0.2” – = 0.125 + 0.0625 + 0.00078125 + 0.000390625 + … – 2進数表現では(とりえず16bitで) “0.0011001100110011” – 小数点以下を固定小数として取り出す “13037” – 固定小数なので、2^16である“65536”が1を表す • 13037÷65536=0.199996948242188≒0.2 (´・ω・`)Shobomaru 8
  • 9.
    固定小数の掛け算 • 例:“8÷5” – = 8×13037÷65536 • この除算は16bit論理右シフトで代用可能 “(8*13037) >> 16” – = 1.5999755859375 – 整数でキャストして答えは“1” _,,-―=''' ̄ ___,,-―――='' ̄ __,-―='' ̄ / _,,-―=''' ̄ _,,-―='' ̄ ヽ / +  ̄ ̄ _,,-―=''' ̄ \ / . . . . ,,-='' ̄ _ノ ,_ノ ヽ / . 。. ★ ☆ ,,,-'' / iニ)ヽ, /rj:ヽヽ ヽ/ 。. . -―'' ̄ ;〈 !:::::::c! |___,/' {.::::::;、! } | -┼- 丿~~~| |~~~~~| __ ■ . |. (つ`''" | / `'ー''(つ. |. -┼- /~~~~/ 丿 | 丿 ▼ ▼ | . ///// | / /// | | 丿 / 丿 ● ● ヽ γ´~⌒ヽ. | / / ――ヽ / ヽ | / /⌒ヽ、 \/ | |_/ / ヽ (´・ω・`)Shobomaru 9
  • 10.
    精度の問題 • この方法で“10÷5”を計算すると… –10×13037÷65536 – = 1.99996948242188 – 整数でキャストして“1”…??? ,,-―=''' ̄ ___,,-―――='' ̄ __,-―='' ̄ / _,,-―=''' ̄ _,,-―='' ̄ ヽ / +  ̄ ̄ _,,-―=''' ̄ \ / . . . . ,,-='' ̄ _ノ ,_ノ ヽ / . 。. ★ ☆ ,,,-'' / iニ)ヽ, /rj:ヽヽ ヽ/ 。. . -―'' ̄ ;〈 !:::::::c! ' {.::::::;、! 〉 |  ̄ ̄| _|_ 丿 |~~~~~| . | (つ`''" __ `'ー''(つ | | | /|. 丿 | 丿 | ///// | | /// | __| 丿 | / 丿 ヽ γ´~⌒ヽ. / | / ――ヽ / ヽ / | /⌒ヽ、 \/ | | ̄ ̄ ̄ ̄| / ヽ • なぜ? – 逆数の丸め誤差を考慮していないから (´・ω・`)Shobomaru 10
  • 11.
    Terje Mathisenのアルゴリズム(?) [1] •逆数の小数点の位置をできるだけ右にずらす – 代償として、乗算後の論理右シフトの量を調整する xを割られる数、dを割る数とするとき、 b = (有効ビット数) – 1 r=w+b f = 2r / d もしfが整数ならば、case Aへ もしfの小数部が0.5未満ならば、case Bへ もしfの小数部が0.5を超えるならば、case Cへ case A: result = x SHR b case B: result = ( ( x + 1 ) * f ) SHR r ただし、fは切り捨て case C: result = ( x * f ) SHR r ただし、fは切り上げ SHRは論理右シフトのこと (´・ω・`)Shobomaru 11
  • 12.
    C言語のプログラム(1) 引数 意味 int short_rcp( div 割る数 unsigned short div, rcp 逆数 unsigned short *rcp, int *shift, shift 論理右シフト量 unsigned short *bias ) bias 補正 { int b = 0; 戻り値 divが2の冪乗か否か for( int i = 0; i < 16; i++ ) { if( ( ( div >> ( 15 - i ) ) & 0x1 ) == 1 ) { b = 15 - i; break; } } unsigned int r = 16 + b; unsigned int r2 = 1 << r; double f = (double)r2 / div; double fm = fmod( f, 1.0 ); (´・ω・`)Shobomaru 12
  • 13.
    C言語のプログラム(2) if( fm == 0.0 ) { *shift = b; *rcp = 1; *bias = 0; return 1; } else if( fm < 0.5 ) { *shift = b; *rcp = (unsigned short)f; *bias = 1; return 0; } else { *shift = b; *rcp = (unsigned short)( f + 0.5 ); *bias = 0; return 0; } } (´・ω・`)Shobomaru 13
  • 14.
    C言語のプログラム(3) const unsigned short dividend = 10; const unsigned short divisor = 5; unsigned short rcp; int shift; unsigned short bias; int ans; int pow2 = short_rcp( divisor, &rcp, &shift, &bias ); if( pow2 ) ans = dividend >> shift; else ans = ( ( dividend + bias ) * rcp ) >> ( shift + 16 ); • ansは期待通り”2” • 割られる数が32769以上のとき、不正な解を出す – C言語の整数拡張ルールの関係で、乗算が符号付きに なってしまう – 楽しいアセンブラプログラミングが待ち受ける (´・ω・`)Shobomaru 14
  • 15.
    逆数求めるの面倒すぎじゃね? • だから言っただろう、 「ただし、除数が固定なら」と。 – 面倒な計算も初回だけなら我慢できる • 除数が固定なら、変数pow2も不変なので、 条件分岐のコストは考えなくてよい – Branch Target Bufferのない糞CPUなんぞ知らん • 整数拡張もSSEなら自分で操作できる – アセンブラいらない! (´・ω・`)Shobomaru 15
  • 16.
    SSE2を使ったプログラム __m128i mdivident; __m128i mrcp = _mm_set1_epi16( rcp ); __m128i mbias = _mm_set1_epi16( bias ); __m128i mans; mdivident = _mm_load_si128( [メモリアドレス] ); if( pow2 ) mans = _mm_srli_epi16( mdibident, shift ); else mans = _mm_srli_epi16( _mm_mulhi_epu16( _mm_add_epi16( mdivident, mbias ), mrcp ), shift ); • _mm_mulhi_epi16 ()は乗算後の上位ビットを返す – 上位型への拡張は要らない – 【悲報】上位ビットを返す乗算は、符号つきorなしの 16bit整数しかない • 8bitなら16bitにunpack、32bitは終了 (´・ω・`)Shobomaru 16
  • 17.
    割る数がUSHORT_MAXのときの問題 • biasが1、divisorが65535のとき、直後の加算で 整数オーバーフローによって不正な解になる • どうしようもないので、 分岐してスカラで計算するか、 型昇格するかで回避するしかない – できたらUSHORT_MAXが来ないようにする (´・ω・`)Shobomaru 17
  • 18.
    実は賢いコンパイラ • 実は、C言語で定数の除算式を書くと 勝手に乗算+論理右シフトにしてくれる – 割る数が2の冪乗なら右シフトだけ volatile unsigned int dividend = 8; unsigned int ans = dividend / 5; mov ecx, *** mov eax, 0CCCCCCDh mul eax, ecx shr edx, 2 (Visual C++ 10.0 / Release) (´・ω・`)Shobomaru 18
  • 19.
    BSR命令を使った最適化(2) • 実は、有効ビット数の計算はx86専用命令がある • BSR命令 – ただし、0(立っているビットがない)は未定義値 • 除算なので、そもそも逆数に0が入ってくる時点でおかしい – assert()なりthrowなり自分で例外処理する • 0が未定義でないLZCNT命令もあるが、AMD専用(SSE4a) – イントリンシック命令 • _BitScanReverse() (Visual C++) ※intrin.hをinclude • _bit_scan_reverse() (Intel C++ Compiler) • __builtin_clz() (GNU gcc) – ARMとかMIPSとかでも使える – xor 16を取る必要あり?(要確認) (´・ω・`)Shobomaru 19
  • 20.
    BSR命令を使った最適化(2) unsigned long bl; _BitScanReverse(&bl, div ); //int b = 16; //for( int i = 0; i < 16; i++ ) { // if( ( ( div >> ( 15 - i ) ) & 0x1 ) == 1 ) { // b = 15 - i; // break; // } //} int b = bl; • といっても逆数を求める部分なので、 効果はほとんどない (´・ω・`)Shobomaru 20
  • 21.
    _mm_set1_epi16() • 実は複数の命令に変換されてしまう – mov + punpcklwd + pshufd • SSSE3なら_mm_shuffle_pi8()、 AVX1なら_mm_broadcastw_epi16() でpunpcklwdは不要になる – movは消せないけど、IvyBridgeからmovはリネームス テージで消滅するらしいから、多分気にしないでいい • AMD?なにそれおいしいの? (´・ω・`)Shobomaru 21
  • 22.
  • 23.
    テーブル参照 • 除数固定、入力値の範囲が大きくなければ、 SSEの代わりに除算結果のテーブルを 作ってしまうのもアリ – テーブル参照なので、SSEは使えない (´・ω・`)Shobomaru 23
  • 24.
    問題点 • SSE/AVXの乗算は16/32bit型だけ –8bit型はpack/unpackで16bitに変換する必要あり • 符号つき整数は一工夫必要 – 乗算を符号付き、シフトを算術シフトにすればいい? • 試すのマンドクセ(‘A`) (´・ω・`)Shobomaru 24
  • 25.
    ARM NEONでは… • NEONも整数の除算はない •乗算はある – VQDMULH命令 • ただし、符号つき16/32bitのみ… – VMULL命令 • 符号なし8/16/32bit、後で自分で上位ビットを取り出す • なぜか整数の逆数命令もある – VRECPE/VRECPS命令 • 符号なし32bit(と32bit小数) – 精度とかはよく知らない • Newton-Raphson法が必要?試すのマンドクセ(‘A`) (´・ω・`)Shobomaru 25
  • 26.
    まとめ • SSEに整数の除算命令はない • なんとか逆数を作ることで、 乗算を使って除算の代用が可能 • それでも制約がいっぱい – 早く整数除算命令を作ってくれ (´・ω・`)Shobomaru 26
  • 27.
    参考文献 1. Optimizing subroutinesin assembly language http://www.agner.org/optimize/optimizing_assem bly.pdf • というか、ほぼパクリです。すみません。 (´・ω・`)Shobomaru 27
  • 28.
    ライセンス • このスライドは全て、 「クリエイティブ・コモンズ 表示 2.1」 の下で提供しています (ただし引用した図・文字を除く) (´・ω・`)Shobomaru 28