タスクグラフとスケジューリング手法

Pythonで学ぶDaskによる並列プログラミング

James Fulton

Climate Informatics Researcher

タスクグラフの可視化

# 2つのdelayedオブジェクトを作成
delayed_num1 = delayed(my_square_function)(3)
delayed_num2 = delayed(my_square_function)(4)

# 加算
result = delayed_num1 + delayed_num2

# タスクグラフを描画 result.visualize()

結果を計算する手順の図。my-square-functionが2回実行され、その出力2つがadd関数に渡され、1つの出力が得られる。

Pythonで学ぶDaskによる並列プログラミング

重なり合うタスクグラフ

delayed_intermediate = delayed(my_square_function)(3)

# 2つの結果はどちらも delayed_intermediate_result を使用
delayed_result1 = delayed_intermediate - 5
delayed_result2 = delayed_intermediate + 4
Pythonで学ぶDaskによる並列プログラミング

重なり合うタスクグラフ

delayed_result1.visualize()

結果1のタスクグラフを示す図。

delayed_result2.visualize()

結果2のタスクグラフを示す図。

Pythonで学ぶDaskによる並列プログラミング

重なり合うタスクグラフ

# タスクグラフを描画
dask.visualize(delayed_result1, delayed_result2)

結果1と結果2が中間結果を共有していることを示すタスクグラフ。

Pythonで学ぶDaskによる並列プログラミング

マルチスレッド vs. 並列処理

データの移動

並列処理
  • プロセスは独自のRAM空間を持つ
マルチスレッド
  • スレッドは同じRAM空間を共有する
Pythonで学ぶDaskによる並列プログラミング

マルチスレッド vs. 並列処理

# 2つの大きな配列を合計
sum1 = delayed(np.sum)(big_array1)
sum2 = delayed(np.sum)(big_array2)

# プロセスで計算
dask.compute(sum1, sum2)
  • 並列処理だと遅い

図は、1つのPythonプロセスにある2つの配列を2つの別プロセスへ送る必要があることを示す。

Pythonで学ぶDaskによる並列プログラミング

マルチスレッド vs. 並列処理

# 2つの大きな配列を合計
sum1 = delayed(np.sum)(big_array1)
sum2 = delayed(np.sum)(big_array2)

# スレッドで計算
dask.compute(sum1, sum2)
  • マルチスレッドなら速い

図は、2つの配列をコピーする必要がないことを示す。

Pythonで学ぶDaskによる並列プログラミング

GIL(グローバルインタプリタロック)

グローバルインタプリタロック(GIL)— 同時にコードを読むのは1スレッドのみ

def sum_to_n(n):
    """0からnまでの合計"""
    total = 0
    for i in range(n+1):
        total += i
    return total
  • ここではマルチスレッドは効果なし
  • 並列処理は有効
sum1 = delayed(sum_to_n)(1000)
sum2 = delayed(sum_to_n)(1000)
Pythonで学ぶDaskによる並列プログラミング

実行時間の例 - GIL

3つのガントチャート。単純なPython関数を16回実行したタイミング。3つのスケジューラの中で、プロセスが最速だった。

Pythonで学ぶDaskによる並列プログラミング

GILを解放する関数

  • 例:pd.read_csv() はGILを解放する
df1 = delayed(pd.read_csv)('file1.csv')
df2 = delayed(pd.read_csv)('file2.csv')
Pythonで学ぶDaskによる並列プログラミング

実行時間の例 - データ読み込み

3つのガントチャート。CSVからデータを読み込む関数を16回実行したタイミング。3つのスケジューラの中で、スレッドが最速だった。

Pythonで学ぶDaskによる並列プログラミング

まとめ

スレッド

  • 初期化が非常に速い
  • メインセッションとメモリ空間を共有
  • メモリ転送が不要
  • 同時にコードを読むのは1スレッド(GIL制約)

プロセス

  • 立ち上げに時間とメモリが必要
  • メモリはプロセスごとに独立
  • 相互・メイン間のデータ転送が非常に遅い
  • それぞれ独自のGILを持ち、順番待ち不要
Pythonで学ぶDaskによる並列プログラミング

Let's practice!

Pythonで学ぶDaskによる並列プログラミング

Preparing Video For Download...