NumPy CPU ディスパッチャー:理解すれば最強のパフォーマンスツール


CPUディスパッチャーの動作原理

CPUディスパッチャーは、マルチソースコンパイルと呼ばれる手法に基づいています。これは、異なるコンパイラフラグと、コードパスに影響を与えるC定義を組み合わせて、特定のソースコードを複数回コンパイルすることを意味します。このプロセスは以下の3つのフェーズに分けられます。

  1. 構成:
    • ビルドオプションを使用して、CPUディスパッチャーの動作を構成します。
      • --cpu-baseline: 最小限必要な最適化セットを指定します。
      • --cpu-dispatch: 追加の最適化のために適用されるセットを指定します。
    • デフォルトでは、--cpu-dispatchmax -xop -fma4に設定されており、AMDレガシー機能を除くすべてのCPU機能を有効にします。
  2. 要求された最適化の検証:
    • ターゲットCPUでサポートされている最適化のみを有効にします。
    • 無効な最適化オプションを警告またはエラーとして報告します。

上記のプロセスを経て、CPUディスパッチャーはターゲットCPUに適した最適化されたコードパスを生成します。

CPUディスパッチャーの利点

CPUディスパッチャーを使用する主な利点は以下の通りです。

  • 柔軟性: ビルドオプションを使用して、特定のニーズに合わせた最適化を構成できます。
  • 移植性: 異なるCPUアーキテクチャ上で動作するコードを容易に生成できます。
  • パフォーマンス向上: ターゲットCPUに合わせた最適化により、NumPy演算のパフォーマンスが大幅に向上します。

以下に、CPUディスパッチャーがどのように動作するかを示す例をいくつか紹介します。

  • ARM CPU:
    • NEONなどのSIMD命令セットをサポートします。
    • CPUディスパッチャーは、これらの命令セットを利用して、ベクトル化された浮動小数点演算と整数演算を高速化します。
  • AMD CPU:
    • SSE、SSE2、AVX、AVX2などのSIMD命令セットをサポートします。
    • CPUディスパッチャーは、これらの命令セットを利用して、ベクトル化された浮動小数点演算と整数演算を高速化します。
  • Intel CPU:
    • AVX、AVX2、AVX512などのSIMD命令セットをサポートします。
    • CPUディスパッチャーは、これらの命令セットを利用して、ベクトル化された浮動小数点演算を高速化します。


CPU 情報の取得

以下のコードは、cpuinfo モジュールを使用して、現在使用しているCPUに関する情報を取得します。

import cpuinfo

print(cpuinfo.get_cpu_info())

このコードを実行すると、CPUの名前、アーキテクチャ、クロック速度、キャッシュサイズなどの情報が表示されます。

SIMD 命令セットの検出

以下のコードは、detect_simd モジュールを使用して、現在の環境でサポートされている SIMD 命令セットを検出します。

import detect_simd

print(detect_simd.detect_simd())

このコードを実行すると、AVX、AVX2、SSE、SSE2などのサポートされている SIMD 命令セットが表示されます。

CPU ディスパッチャーによるコードのコンパイル

以下のコードは、numpy.dispatch モジュールを使用して、特定の CPU ディスパッチャー構成で NumPy コードをコンパイルします。

import numpy as np
from numpy.dispatch import Dispatcher

dispatcher = Dispatcher(cpu_baseline='avx2', cpu_dispatch='avx512')

@dispatcher
def my_function(a, b):
    # アレイ a と b を処理するコード
    return a + b

# コードを実行
result = my_function(np.ones(10), np.ones(10))
print(result)

このコードでは、avx2 ベースラインと avx512 ディスパッチ構成を使用して my_function 関数をコンパイルします。この関数は、AVX512 命令セットがサポートされている場合は AVX512 命令セットを使用して、そうでなければ AVX2 命令セットを使用して実行されます。

カスタム SIMD コードの作成

以下のコードは、numpy.vectorize モジュールを使用して、カスタム SIMD コードを作成する方法を示します。

import numpy as np
from numpy.vectorize import vectorize

def my_simd_function(a, b):
    # SIMD 命令を使用してアレイ a と b を処理するコード
    return a + b

# コードをベクトル化
simd_function = vectorize(my_simd_function)

# コードを実行
result = simd_function(np.ones(10), np.ones(10))
print(result)

このコードでは、my_simd_function 関数を vectorize 関数を使用してベクトル化します。このベクトル化された関数は、SIMD 命令を使用してアレイを処理します。

  • NumPy の最新バージョンには、CPU ディスパッチャーと SIMD コードに関するより多くの機能が含まれています。詳細については、NumPy の公式ドキュメントを参照してください。
  • 上記のコード例はあくまでも説明目的であり、実際の使用状況に合わせて調整する必要があります。


NumPy CPU ディスパッチャーを道路整備に例える

想像してみてください。あなたは新しい高速道路を建設する責任者です。さまざまな種類の車 (乗用車、トラック、バスなど) が道路を走行する必要があります。それぞれの車には、異なる速度と走行特性があります。

高速道路を効率的に運用するために、あなたは車の種類に応じて異なる車線を設けます。乗用車は一般車線、トラックは専用車線、バスはバスレーンといった具合です。それぞれの車線は、車種の特性に合わせて設計されており、安全かつスムーズな通行を実現します。

NumPy CPU ディスパッチャーも同じような役割を果たします。CPU は、さまざまな種類の命令 (浮動小数点演算、整数演算、論理演算など) を処理する能力を持っています。しかし、それぞれの命令には、異なる処理速度と特性があります。

CPU ディスパッチャーは、命令の種類に応じて異なる処理パスを設けることで、CPU のパフォーマンスを最大限に引き出します。浮動小数点演算は専用の演算ユニットで処理され、整数演算は別のユニットで処理されます。

例:高速道路と CPU ディスパッチャーの比較

項目高速道路CPU ディスパッチャー
目的車種の特性に合わせて効率的な通行を実現する命令の種類に応じて効率的な処理を実現する
方法車種ごとに異なる車線を設ける命令の種類ごとに異なる処理パスを設ける
利点安全性と通行量の増加CPU パフォーマンスの向上

NumPy CPU ディスパッチャーは、CPU の能力を最大限に引き出すために不可欠なメカニズムです。道路整備に例えることで、その仕組みをより直感的に理解することができます。