Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

拡張命令リファレンス

前提知識: SIMD 概論拡張命令の歴史

このページの要点

  • SSE2 の 128 ビット論理演算(por, pand, pxor, pandn)はビットボードの基礎命令
  • SSSE3 の pshufb(バイトシャッフル)はバイト逆順操作に使い、飛び利き計算で活躍する
  • POPCNT と TZCNT/LZCNT はビットスキャンの中核であり、指し手列挙で毎回呼ばれる
  • PEXT/PDEP(BMI2)は Magic Bitboard の代替となる強力な命令だが CPU 互換性に注意が必要
  • AVX2 は 256 ビット整数演算を提供し、複数ビットボードの一括処理や NNUE 推論で使われる

命令一覧

本ページで解説する命令を用途別に分類します。

用途命令命令セット主な使用場面
論理演算por, pand, pxor, pandnSSE2ビットボード合成・マスク
シフトpsllq, psrlqSSE2歩の利き計算、ビット移動
比較pcmpeqdSSE2ゼロ判定
テストptestSSE4.1ビットボードの交差判定
シャッフルpshufbSSSE3バイト逆順(byte_reverse)
ビットカウントpopcntPOPCNT駒数カウント、手数見積もり
ビットスキャンtzcntBMI1LSB 検出(指し手列挙)
ビットスキャンlzcntBMI1MSB 検出
ビット抽出pextBMI2飛び利きの直接計算
ビット挿入pdepBMI2PEXT の逆操作
256bit 論理演算vpor, vpandAVX2複数ビットボードの一括処理
512bit 演算vporqAVX-512NNUE 推論の高速化

SSE2: 128 ビット整数論理演算

SSE2 はビットボード演算の最も基本的な命令セットです。 x86-64 の必須仕様であり、すべての 64 ビット x86 CPU で利用可能です。

論理演算

128 ビット幅の論理演算は、ビットボードの合成・マスク適用・差分更新で頻繁に使われます。

intrinsicアセンブリ動作用途例
_mm_or_si128(a, b)pora OR b先手・後手のビットボード合成
_mm_and_si128(a, b)panda AND bマスク適用(特定の段・筋の抽出)
_mm_xor_si128(a, b)pxora XOR b差分更新(駒の移動)
_mm_andnot_si128(a, b)pandn(NOT a) AND bマスクの除外

注意: _mm_andnot_si128(a, b)(NOT a) AND b であり、a AND (NOT b) ではありません。 引数の順序に注意が必要です。

OR: 駒の配置を合成 先手駒 | 後手駒 = 全駒

AND: 特定範囲の抽出 全駒 & 段マスク = その段にいる駒

XOR: 差分更新(駒の移動)
  盤面 ^ (移動元 | 移動先) = 移動後の盤面

シフト演算

SSE2 のシフト命令は 64 ビット要素単位で動作します。 128 ビット全体としてのシフトではない点に注意が必要です。

intrinsicアセンブリ動作
_mm_slli_epi64(a, n)psllq各 64bit 要素を左に n ビットシフト
_mm_srli_epi64(a, n)psrlq各 64bit 要素を右に n ビットシフト
_mm_slli_si128(a, n)pslldq128bit 全体を左に n バイトシフト
_mm_srli_si128(a, n)psrldq128bit 全体を右に n バイトシフト

128 ビット全体のビット単位シフトを実現するには、要素間の桁上げを手動で処理する必要があります。

128bit 左シフト(n < 64 の場合): hi’ = (hi << n) | (lo >> (64 - n)) ← lo からの桁上げ lo’ = lo << n

128bit 左シフト(64 ≤ n < 128 の場合):
  hi' = lo << (n - 64)
  lo' = 0

歩の利き計算では「全体を 1 マス分シフト」する操作が頻出しますが、ビットボードのレイアウトによっては筋方向のシフトが 64 ビット要素をまたぐため、この桁上げ処理が必要になります。

比較・ゼロ判定

intrinsicアセンブリ動作
_mm_cmpeq_epi64(a, b)pcmpeqq164bit 要素ごとの等値比較
_mm_movemask_epi8(a)pmovmskb各バイトの最上位ビットを 16bit マスクに集約

ビットボードがゼロかどうかの判定は、_mm_cmpeq_epi64 でゼロレジスタと比較し、_mm_movemask_epi8 で結果を集約する方法が SSE2 の範囲で可能です。 ただし SSE4.1 の _mm_testz_si128 を使えばより効率的です。

SSSE3: バイトシャッフル

SSSE3 で追加された _mm_shuffle_epi8 は、128 ビットレジスタ内のバイトを任意の順序に並べ替える強力な命令です。

intrinsicアセンブリ動作
_mm_shuffle_epi8(a, mask)pshufbmask に従ってバイト単位で並べ替え

byte_reverse への応用

ビットボードのバイト逆順操作(byte_reverse)は、pshufb_mm_shuffle_epi8)1 命令で実現できます。 この操作は rsshogi が採用する Qugiy 方式の飛び利き計算で、逆方向のビーム処理に必要です。

入力:  [b15 b14 b13 b12 b11 b10 b9 b8 | b7 b6 b5 b4 b3 b2 b1 b0]
mask:  [  0   1   2   3   4   5  6  7 |  8  9 10 11 12 13 14 15]
出力:  [b0  b1  b2  b3  b4  b5 b6 b7 | b8 b9 b10 b11 b12 b13 b14 b15]

rsshogi は環境に応じて 3 つの実装パスを持ちます。

  • SSSE3 あり(128bit): Bitboard::byte_reverse()_mm_shuffle_epi8pshufb)を使用。
  • AVX2(256bit): U64x4::byte_reverse_simd()_mm256_shuffle_epi8vpshufb)で各 128bit レーン内を逆順化。
  • SSSE3 非対応: u64x2_ops::byte_reverse が各 64bit を u64::swap_bytes() で逆順にするスカラー実装にフォールバック。

つまり SSSE3 非対応環境でもスカラー実装で正しく動作します。

SSE4.1: テスト命令

intrinsicアセンブリ動作
_mm_testz_si128(a, b)ptest(a AND b) == 0 なら ZF=1

ptest は 2 つの 128 ビット値の AND を取り、結果がゼロかどうかをフラグレジスタに反映します。 値そのものを書き出さずにフラグだけを更新するため、条件分岐と組み合わせて効率的に使えます。

主な用途

// ビットボードがゼロかどうか
fn is_empty(bb: Bitboard) -> bool {
    // ptest xmm0, xmm0 → ZF で判定
    _mm_testz_si128(bb.m, bb.m) != 0
}

// 2 つのビットボードに共通のビットがあるか
fn has_intersection(a: Bitboard, b: Bitboard) -> bool {
    // ptest xmm0, xmm1 → (a & b) == 0 なら交差なし
    _mm_testz_si128(a.m, b.m) == 0
}

is_emptyhas_intersection は指し手生成と合法性チェックの最内周ループで呼ばれるため、1 命令で完結することの効果は大きいです。

POPCNT: ビットカウント

intrinsicアセンブリ動作
popcnt(x)popcntセットされたビットの数を返す

POPCNT は 64 ビット整数中の 1 のビット数を数える専用命令です。 Intel では SSE4.2 と同時に Nehalem(2008)で導入され、AMD では ABM 拡張として Phenom II(2007)で先行導入されました。

性能特性

CPUレイテンシスループット
Intel Nehalem+3 サイクル1 サイクル
AMD Zen+1 サイクル1 サイクル

ソフトウェア実装(ビットカウントの分割加算)と比較して約 2〜5 倍高速です。

主な用途

  • 駒数のカウント: あるビットボードに含まれる駒の数
  • 指し手候補数の見積もり: 利きビットボードの POPCNT で指せるマス数を取得
  • SEE(Static Exchange Evaluation): 交換の勝ち負けを判定する際の攻撃駒数カウント
  • 評価関数: 駒の配置パターンの評価

128 ビットビットボードに対しては、上位・下位の各 64 ビットに popcnt を適用し、結果を加算します。

fn count_ones(bb: Bitboard) -> u32 {
    bb.lo().count_ones() + bb.hi().count_ones()
    // → popcnt rax, [bb]
    //   popcnt rcx, [bb+8]
    //   add eax, ecx
}

BMI1: TZCNT / LZCNT / ANDN

BMI1(Bit Manipulation Instruction Set 1)は、ビット操作を効率化する命令群です。 Intel Haswell(2013)と AMD Piledriver(2012)で導入されました。

TZCNT(Trailing Zero Count)

intrinsicアセンブリ動作
_tzcnt_u64(x)tzcnt最下位セットビット(LSB)の位置を返す

TZCNT は最下位の 1 ビット(LSB)の位置を返します。 ビットボードから指し手を 1 つずつ取り出す「pop LSB」操作の中核です。

入力: 0b0000_0000_0010_1000
                       ^
結果: 3  (ビット 3 が最下位の 1)

LZCNT(Leading Zero Count)

intrinsicアセンブリ動作
_lzcnt_u64(x)lzcnt最上位セットビット(MSB)からの先行ゼロ数を返す

LZCNT は最上位側の先行ゼロの数を返します。 MSB の位置は 63 - lzcnt(x) で得られます。

ANDN

intrinsicアセンブリ動作
_andn_u64(a, b)andn(NOT a) AND b

SSE2 の pandn の 64 ビットスカラー版です。 ビットのクリアに使われます。

BSF / BSR との違い

BMI1 の TZCNT / LZCNT は、従来の BSF(Bit Scan Forward)/ BSR(Bit Scan Reverse)の改良版です。

命令入力が 0 の場合命令セット
BSF未定義動作x86 基本命令
BSR未定義動作x86 基本命令
TZCNTオペランドサイズを返す(64)BMI1
LZCNTオペランドサイズを返す(64)BMI1

BSF/BSR は入力がゼロの場合に結果が未定義であるため、事前にゼロチェックが必要でした。 TZCNT/LZCNT はゼロ入力に対して定義された値を返すため、分岐なしで安全に使えます。

補足: TZCNT のエンコーディング互換性(クリックで展開)

TZCNT のマシンコードエンコーディングは rep bsf と同じです。 BMI1 非対応 CPU で TZCNT を実行すると、rep プレフィクスが無視されて BSF として動作します。 入力がゼロでない場合は結果が同じであるため、多くの場合は意図せず互換動作します。 ただしゼロ入力時の挙動が異なるため、正確な動作には BMI1 の存在確認が必要です。

BMI2: PEXT / PDEP

BMI2(Bit Manipulation Instruction Set 2)は Intel Haswell(2013)で導入されました。 PEXT と PDEP は将棋エンジンの飛び利き計算を劇的に高速化する可能性を持つ命令ですが、AMD の互換性問題に注意が必要です。

PEXT(Parallel Bits Extract)

intrinsicアセンブリ動作
_pext_u64(src, mask)pextmask の 1 ビット位置から src のビットを抽出し、右詰めで返す

PEXT は、マスクで指定された位置のビットを抽出し、連続した下位ビットとして返します。

src:   1 0 1 1 0 1 0 0
mask:  1 0 1 0 1 0 1 0   ← ビット 7, 5, 3, 1 を抽出
       ↓   ↓   ↓   ↓
結果:  0 0 0 0 1 1 0 0   ← 抽出した 4 ビットを右詰め

PDEP(Parallel Bits Deposit)

intrinsicアセンブリ動作
_pdep_u64(src, mask)pdepsrc の下位ビットを mask の 1 ビット位置に散布する

PDEP は PEXT の逆操作で、下位ビットをマスク位置に散布します。

src:   0 0 0 0 1 1 0 0
mask:  1 0 1 0 1 0 1 0   ← ビット 7, 5, 3, 1 に配置
                          ↓
結果:  1 0 1 0 0 0 0 0   ← src の下位4ビットを散布

飛び利き計算への応用

PEXT を使った飛び利き計算は、従来の Magic Bitboard に代わるアプローチです。

1. 盤面の占有ビットボードから、対象方向の関連マスを PEXT で抽出
2. 抽出結果をインデックスとしてテーブルを参照
3. テーブルから利きビットボードを取得

Magic Bitboard がマジックナンバーの乗算とシフトでインデックスを計算するのに対し、PEXT は直接的にビットを抽出するため、概念的にも実装的にもシンプルです。 ただし AMD Zen2 以前での性能問題があるため、Magic Bitboard との実行時切り替えが実用的な戦略です。 rsshogi は PEXT を使わず Qugiy 方式を採用しており、Zen2 環境でも一貫した性能を発揮します。 各アルゴリズムの比較は飛び利きアルゴリズム比較を参照してください。

性能特性

CPUPEXT レイテンシPEXT スループット
Intel Haswell3 サイクル1 サイクル
Intel Skylake+3 サイクル1 サイクル
AMD Zen〜Zen218+ サイクル18+ サイクル
AMD Zen3+3 サイクル1 サイクル

AVX2: 256 ビット整数演算

AVX2 は Intel Haswell(2013)で導入された 256 ビット整数 SIMD 命令セットです。 256 ビットの __m256i レジスタ(ymm0ymm15)を使用します。

主要命令

intrinsicアセンブリ動作
_mm256_or_si256(a, b)vpor256bit OR
_mm256_and_si256(a, b)vpand256bit AND
_mm256_xor_si256(a, b)vpxor256bit XOR
_mm256_andnot_si256(a, b)vpandn256bit ANDN
_mm256_add_epi64(a, b)vpaddq64bit 要素ごとの加算
_mm256_sub_epi64(a, b)vpsubq64bit 要素ごとの減算
_mm256_slli_epi64(a, n)vpsllq64bit 要素ごとの左シフト
_mm256_srli_epi64(a, n)vpsrlq64bit 要素ごとの右シフト

将棋エンジンでの用途

1. 複数ビットボードの一括処理

256 ビットレジスタに 2 つの 128 ビットビットボードを格納し、1 命令で同時に演算できます。 rsshogi では Bitboard256 がこのパターンを実装しており、角の 4 方向利き計算_mm256_and_si256_mm256_sub_epi64_mm256_xor_si256 を連鎖させて使用します。

2. NNUE 評価関数の推論

NNUE の差分更新と推論には大量のベクトル演算(加算・ClippedReLU など)が含まれます。 AVX2 の 256 ビット整数演算を使えば、16 個の 16 ビット値を 1 命令で処理できます。 これは SSE2(8 個同時)の 2 倍のスループットです。

注意点: レジスタ間のレーン境界

AVX2 の 256 ビットレジスタは内部的に 2 つの 128 ビット「レーン」に分かれています。 多くのシャッフル命令はレーンをまたげないため、128 ビット境界を超えるデータの移動には追加の命令(vperm2i128 等)が必要です。

256bit レジスタ:
[  上位 128bit レーン  |  下位 128bit レーン  ]
  ← レーン内シャッフル OK →← レーン内シャッフル OK →
         ← レーン間移動は追加命令が必要 →

AVX-512: 512 ビット演算

AVX-512 は 512 ビット幅の __m512i レジスタ(zmm0zmm31)を使用する命令セットです。 Intel Skylake-X(2017)で初めて導入され、AMD は Zen4(2022)で対応しました。

特徴

  • レジスタ本数の倍増: 32 本の zmm レジスタ(AVX2 は 16 本の ymm)
  • マスクレジスタ: k0k7 の 8 本のマスクレジスタで要素単位の条件処理
  • 多数のサブ拡張: F, CD, BW, DQ, VL, VBMI, VNNI, BF16 など

サブ拡張の分類

AVX-512 は単一の命令セットではなく、多数のサブ拡張の集合体です。

サブ拡張主な機能対応 CPU
F (Foundation)512bit 基本演算Skylake-X, Zen4
CD (Conflict Detection)衝突検出Skylake-X, Zen4
BW (Byte/Word)8/16bit 要素操作Skylake-X, Zen4
DQ (Doubleword/Quadword)32/64bit 拡張Skylake-X, Zen4
VL (Vector Length)128/256bit への適用Skylake-X, Zen4
VNNI整数ニューラルネット演算Ice Lake, Zen4
BF16BFloat16 演算Cooper Lake, Zen4

将棋エンジンでの用途

AVX-512 の主な用途は NNUE 推論の高速化です。 512 ビット幅で 32 個の 16 ビット値を同時処理できるため、NNUE の差分更新が AVX2 のさらに 2 倍のスループットで実行可能です。

ビットボード演算(128 ビット)に対しては、AVX-512 の恩恵は限定的です。 512 ビットのうち 384 ビットが無駄になるため、ビットボード単体では AVX2 以上の利点はほぼありません。

消費電力とクロックダウン

AVX-512 命令を実行すると、多くの CPU でクロック周波数が低下します。2 これは 512 ビット幅の演算ユニットの消費電力が大きいためです。

状態Intel Skylake-X 例
非 AVX最大ターボクロック
AVX2 使用中約 100〜200 MHz 低下
AVX-512 使用中約 200〜500 MHz 低下

このクロックダウンは AVX-512 を使わないコード(ビットボード演算など)にも影響するため、 AVX-512 を NNUE 推論のみに限定し、頻繁に切り替えが起きないよう設計する配慮が必要です。

AMD Zen4 以降ではクロックダウンが Intel より小さいとされていますが、ゼロではありません。

命令レイテンシ・スループット早見表

代表的な CPU における主要命令の性能をまとめます。3 レイテンシは結果が使えるまでのサイクル数、スループットは 1 サイクルあたりに発行できる命令数の逆数です。

命令Intel SkylakeAMD Zen3備考
por / pand (128bit)1 / 0.331 / 0.25ビットボード基本演算
vpor / vpand (256bit)1 / 0.331 / 0.25AVX2
pshufb (128bit)1 / 0.51 / 0.5byte_reverse
ptest (128bit)2 / 11 / 0.33ゼロ判定
popcnt (64bit)3 / 11 / 1ビットカウント
tzcnt (64bit)3 / 11 / 0.33LSB 検出
lzcnt (64bit)3 / 11 / 0.33MSB 検出
pext (64bit)3 / 13 / 1ビット抽出
pdep (64bit)3 / 13 / 1ビット挿入

数値は「レイテンシ / スループット(サイクル)」形式。 スループットの値が小さいほど、連続実行時のパイプライン効率が高いことを示します。

Rust での SIMD intrinsic の使い方

Rust では std::arch モジュールを通じて SIMD intrinsic を使用します。

コンパイル時の機能ゲート

// ファイル冒頭で条件付きインポート
#[cfg(all(target_arch = "x86_64", target_feature = "sse2"))]
use std::arch::x86_64::{
    __m128i, _mm_or_si128, _mm_and_si128, _mm_xor_si128,
};

#[cfg(all(target_arch = "x86_64", target_feature = "avx2"))]
use std::arch::x86_64::{
    __m256i, _mm256_or_si256, _mm256_and_si256,
};

実行時の機能検出

// 実行時に CPU 機能を検出
if is_x86_feature_detected!("avx2") {
    // AVX2 パスを実行
} else if is_x86_feature_detected!("sse2") {
    // SSE2 フォールバック
}

target_feature 属性

// この関数は AVX2 が有効な環境でのみ呼び出し可能
#[target_feature(enable = "avx2")]
unsafe fn process_avx2(data: &[u8]) {
    // AVX2 intrinsic を使用
}

#[target_feature] を付けた関数は unsafe になります。 呼び出し元で CPU 機能を確認してから呼ぶ責務があるためです。

まとめ

命令セット将棋エンジンでの主要用途必須度
SSE2ビットボード論理演算必須(x86-64 標準)
SSSE3byte_reverse推奨(2011 年以降の全 CPU)
SSE4.1ゼロ判定(ptest)推奨
POPCNTビットカウントほぼ必須
BMI1TZCNT / LZCNT推奨
BMI2PEXT / PDEPIntel 向けオプション(AMD 注意)
AVX2NNUE 推論、一括処理NNUE 使用時は推奨
AVX-512NNUE 推論の最速化オプション


  1. pcmpeqq(64 ビット要素比較)は SSE4.1 で追加。SSE2 では pcmpeqd(32 ビット要素比較)を使い、追加のマスク処理で 64 ビット比較を実現する。

  2. Intel, “Intel 64 and IA-32 Architectures Optimization Reference Manual” — AVX-512 使用時の周波数低下(license reduction)について記載。

  3. Agner Fog, “Instruction tables: Lists of instruction latencies, throughputs and micro-operation breakdowns for Intel, AMD, and VIA CPUs” — https://www.agner.org/optimize/instruction_tables.pdf