PyTorch MPSでマルチスレッドアプリケーションを安全に同期させる! torch.mps.Eventで同期とタイミング制御を行う

2024-06-20

PyTorch MPSにおける「torch.mps.Event」のプログラミング解説

torch.mps.Event は、MPS (Metal Performance Shaders) バックエンドで同期とタイミング制御を行うためのオブジェクトです。MPS ストリームの同期、デバイスの処理状況の監視、コード実行時間の正確な測定などに役立ちます。

機能

  • MPS ストリーム間の同期
  • デバイスの処理状況の監視
  • コード実行時間の正確な測定
  • MPS カーネルの完了を待機
  • MPS カーネルの完了を通知

利点

  • コード実行の同期とタイミング制御を可能にする
  • MPS デバイスのパフォーマンスを向上させる
  • コードのデバッグとプロファイリングを容易にする

使用方法

import torch
import torch.mps as mps

# MPS デバイスを作成
device = torch.device('mps')

# MPS イベントを作成
event = mps.Event(device)

# MPS ストリームを作成
stream = mps.Stream(device)

# MPS カーネルをストリームに割り当て
with torch.cuda.stream(stream):
    # 計算を実行
    tensor1 = torch.randn(1000, device=device)
    tensor2 = torch.randn(1000, device=device)
    result = tensor1 + tensor2

# MPS カーネルが完了するのを待つ
event.record(stream)
event.wait()

# 結果を処理
print(result)

補足

  • torch.mps.Event オブジェクトは、一度作成したら再利用できません。
  • torch.mps.Event オブジェクトは、MPS デバイス上でのみ使用できます。
  • torch.mps.Event オブジェクトは、コンテキストマネージャーとして使用できます。
    • PyTorch MPS は、MacOS デバイスでのみ使用できます。
    • PyTorch MPS は、まだ開発段階であり、すべての機能が利用可能とは限りません。


    サンプル 1: MPS ストリームの同期

    import torch
    import torch.mps as mps
    
    # MPS デバイスを作成
    device = torch.device('mps')
    
    # MPS イベントを作成
    event = mps.Event(device)
    
    # MPS ストリームを作成
    stream1 = mps.Stream(device)
    stream2 = mps.Stream(device)
    
    # MPS カーネルをストリームに割り当て
    with torch.cuda.stream(stream1):
        # 計算を実行
        tensor1 = torch.randn(1000, device=device)
    
    with torch.cuda.stream(stream2):
        # 計算を実行
        tensor2 = torch.randn(1000, device=device)
        result = tensor1 + tensor2
    
    # MPS カーネルが完了するのを待つ
    event.record(stream2)
    event.wait()
    
    # 結果を処理
    print(result)
    

    サンプル 2: デバイスの処理状況の監視

    import torch
    import torch.mps as mps
    import time
    
    # MPS デバイスを作成
    device = torch.device('mps')
    
    # MPS イベントを作成
    event = mps.Event(device)
    
    # MPS ストリームを作成
    stream = mps.Stream(device)
    
    # MPS カーネルをストリームに割り当て
    with torch.cuda.stream(stream):
        # 計算を実行
        tensor1 = torch.randn(1000, device=device)
        tensor2 = torch.randn(1000, device=device)
        result = tensor1 + tensor2
    
    # 開始時間を記録
    start_time = time.time()
    
    # MPS カーネルが完了するのを待つ
    event.record(stream)
    event.wait()
    
    # 終了時間を記録
    end_time = time.time()
    
    # 処理時間を計算
    elapsed_time = end_time - start_time
    
    # 処理時間を表示
    print(f"Processing time: {elapsed_time:.3f} seconds")
    

    サンプル 3: コード実行時間の測定

    import torch
    import torch.mps as mps
    import time
    
    # MPS デバイスを作成
    device = torch.device('mps')
    
    # MPS イベントを作成
    event_start = mps.Event(device)
    event_end = mps.Event(device)
    
    # MPS ストリームを作成
    stream = mps.Stream(device)
    
    # MPS カーネルをストリームに割り当て
    with torch.cuda.stream(stream):
        # 計算を実行
        tensor1 = torch.randn(1000, device=device)
        tensor2 = torch.randn(1000, device=device)
        result = tensor1 + tensor2
    
    # 開始時間を記録
    event_start.record(stream)
    
    # 計算を実行
    result = result * result
    
    # 終了時間を記録
    event_end.record(stream)
    
    # MPS カーネルが完了するのを待つ
    event_start.wait()
    event_end.wait()
    
    # 処理時間を計算
    elapsed_time = event_end.elapsed_time(event_start)
    
    # 処理時間を表示
    print(f"Processing time: {elapsed_time:.3f} seconds")
    

    注:

    • PyTorch MPS はまだ開発段階であり、すべての機能が利用可能とは限りません。


    「torch.mps.Event」の代替方法

    torch.cuda.Event

    • 利点:
      • PyTorch CUDA で広く使用されており、多くの開発者が使い慣れている
      • MPS デバイスだけでなく、CUDA デバイスでも使用できる
    • 欠点:
      • MPS デバイス上でのみ使用できる「torch.mps.Event」に比べて機能が限定されている
      • MPS デバイスの処理状況を監視する機能がない

    torch.synchronize()

    • 利点:
      • シンプルで使いやすい
    • 欠点:
      • 詳細なタイミング制御や同期機能を提供しない
      • 複数のストリームを同期するのに適していない

    Busy-waiting

    • 利点:
      • 最も単純な方法
    • 欠点:
      • CPU リソースを浪費する
      • コードの可読性と保守性を低下させる

    カスタム同期メカニズム

    • 利点:
      • アプリケーションのニーズに合わせた柔軟な同期メカニズムを構築できる
    • 欠点:
      • 複雑で実装が難しい
      • バグが発生しやすい

    最適な代替方法は、アプリケーションの要件と制約によって異なります。

    • シンプルで使いやすい同期が必要な場合は、torch.synchronize() が良い選択です。
    • より詳細なタイミング制御や同期機能が必要な場合は、torch.cuda.Event を検討してください。
    • MPS デバイスの処理状況を監視する必要がある場合は、「torch.mps.Event」が唯一の選択肢です。
    • パフォーマンスが重要で、CPU リソースを節約したい場合は、カスタム同期メカニズムを検討する必要があります。
    • アプリケーションがマルチスレッドで実行されている場合は、同期メカニズムがスレッドセーフであることを確認する必要があります。
    • デッドロックを避けるために、同期メカニズムを慎重に使用してください。