Skip to content
Go back

非同期IO

· Updated:

同期IO (Windows)

  • ワーカースレッドに逃がせば非同期的に扱うこともできなくはない
  • スレッドごとにハンドルを用意しないと、パフォーマンスが低下する?

Overlapped IO (Windows)

  • 同期IOのAPIをそのまま活用する
  • 条件や状況によっては非同期にならない場合がある

API

  • CreateFile
    • ファイルを作ったり開いたりする
    • FILE_FLAG_OVERLAPPEDフラグを加えると、そのファイルのIOが非同期になる
  • ReadFile
    • データを読み出す
    • オフセット値や完了通知用イベントをOVERLAPPEDで渡す
      • イベントは、処理開始時にリセットされ、処理完了時にセットされる
    • IO処理がその場で完了すると、TRUEを返す
      • このとき、読み出したバイト数も一緒に返される
    • IO処理が完了しなければ、FALSEを返す
      • 正常に保留された場合、GetLastErrorERROR_IO_PENDINGを返す
  • GetOverlappedResult
    • IO処理が完了したか調べる
    • 完了時、読み出したバイト数も一緒に返す
  • CancelIoEx
    • IO処理を取り消す
    • 指定のファイルでのIO処理すべてを取り消すこともできる

メモ

  • OVERLAPPEDオブジェクトは処理が完了するまで、変更・解放・再利用のいずれもしてはいけない
    • すなわち、OVERLAPPEDを格納したメモリは指定のIO処理専用として寿命をともにすると良い?
  • OVERLAPPEDのイベントハンドルはNULLにもできるが非推奨
    • そうした場合、ファイルハンドルで完了通知を行うため、個々のIO処理の完了を認識できなくなる
  • 非同期IOとして呼び出しても同期IOになる場合がある:
    • NTFS圧縮やNTFS暗号化されたストレージは非同期にアクセスできない
    • サイズ延長を伴う書き込みは同期的に行われる
  • キャッシュを介すると、同期IOになる場合がある:
    • キャッシュにヒットした場合、即座に制御が返るので同期IOのように見える
    • キャッシュにヒットしなかった場合、ページフォルト機構は同期的に行われる
      • 処理をワーカースレッドに逃がしたりもするが、用意されるスレッド数は多くないため、立て続けにIO処理を投げると同期処理になる
  • 必ず非同期にしたいなら、キャッシュを通らなければ良い
    • FILE_FLAG_NO_BUFFERINGフラグを立てると、データをメモリにキャッシュしなくなる
    • その場合、オフセットやバッファメモリのアライメントをストレージに揃える必要がある

IO Ring (Windows)

  • Windows 11の21H2 (Build 22000)で導入された新しいAPI
  • 事前準備や一括処理によってオーバーヘッドを削減できる
  • 完了通知も一括して行われるため、サブミットの粒度や頻度に気をつける必要がある

API

  • CreateIoRing
    • サイズを指定して、キューを生成する
    • 処理提出用と完了通知用の2つのキューがある
  • BuildIoRingRegisterBuffers/BuildIoRingRegisterFileHandles
    • バッファメモリ/ファイルハンドルのリストを登録する処理をキューに追加する
    • 登録の際、以前に登録したリストは完全に上書きされて利用できなくなる
  • BuildIoRingReadFile
    • データを読み出す処理をキューに追加する
    • 登録済みのバッファメモリ/ファイルハンドルをリストのインデックスで指定できる
  • SubmitIoRing
    • キューに追加した処理をカーネルに提出する
    • 指定数の処理を完了する、または、指定時間を経過する、まで待機できる
  • BuildIoRingCancelRequest
    • 処理を取り消す処理をキューに追加する
    • 設定されたuserDataの値と一致する処理を対象とする
  • PopIoRingCompletion
    • 完了通知をキューから取り出す
    • 処理の識別は設定されたuserDataの値から行う
  • SetIoRingCompletionEvent
    • 完了通知キューが空でなくなったことを通知するためのイベントをセットする
    • セットできるイベントは最大1つ
    • イベントハンドルは内部で複製されるので、ユーザー側は閉じても良い

メモ

  • バッファメモリやファイルハンドルを事前に登録することで、検証によるオーバーヘッドを登録時の一度だけにできる
  • 複数のIO処理を一度のシステムコールで発行できるので、従来のAPIと比較してオーバーヘッドを削減できる
  • 提出される処理はuserDataで渡す値を一意にすることで個体を識別できるようになる
  • Linuxにおけるio_uringというほぼ同等の機能をもとに作られているため、マルチプラットフォーム対応がし易い、かもしれない

参考文献