ベクトル化
要素ごとのループを、配列全体の演算として書き直す。
python
# ループ — 遅い
result = []
for x in data:
result.append(x * 2 + 1)
# ベクトル化 — 速い
result = data * 2 + 1なぜ速いのか#
Python のループでは、1 要素ごとに
- バイトコードのディスパッチ
- オブジェクトの型判定
- メソッド呼び出し
- 結果オブジェクトの確保
が起きる。計算そのものより付随処理が支配的。
ベクトル化すると、これらが 1 回で済み、 実際のループは C の中で回る。 さらに SIMD も効く。
10〜100 倍の差がつくのが普通。
代償#
| 代償 | 内容 |
|---|---|
| メモリ | 中間結果の配列が確保される |
| 可読性 | 複雑な条件分岐は書きにくい |
| 早期終了できない | 条件を満たしたら止める、が書けない |
python
a * 2 + b * 3 + c # 中間配列が 3 つできる巨大配列ではメモリが問題になる。
np.add(a, b, out=a) のような in-place 演算や、
NumExpr、Numba で融合させる手がある。
ベクトル化しにくいもの#
- 前の反復の結果に依存する(漸化式)
- 要素ごとに違う分岐をする
- 可変長の処理
こうした場合は Numba(JIT)、Cython、 あるいは C 拡張に降りる。
より広い意味#
「ベクトル化」は CPU の SIMD 命令を指すこともある。 NumPy の意味では **「ループを高速な実装に委ねる」**という より抽象的な原則を指す。
深層学習でバッチ処理をするのも、 GPU に 大きな行列演算として渡すためで、同じ発想。
参考文献#
- Charles R. Harris et al. Array programming with NumPy. Nature 585, 2020. https://doi.org/10.1038/s41586-020-2649-2
- NumPy documentation. Broadcasting. https://numpy.org/doc/stable/user/basics.broadcasting.html
- Siu Kwan Lam, Antoine Pitrou, Stanley Seibert. Numba: a LLVM-based Python JIT compiler. LLVM-HPC, 2015. https://doi.org/10.1145/2833157.2833162