DirectML での UAV バリアとリソース状態バリア
順序指定されていないアクセス ビュー (UAV) バリアの要件
Direct3D 12 での UAV バリア
Direct3D 12 では、同じコマンド リスト内の隣接計算シェーダー ディスパッチは、介在する順序指定されていないアクセス ビュー (UAV) バリアと同期している場合を除いて、GPU で並列に実行することが許可されます。 これにより、GPU ハードウェアの使用率を増やすことで、パフォーマンスを向上させることができます。 ただし、既定では、UAV バリアを使用しない場合、2 つの隣接ディスパッチ間にデータの依存関係が存在するか、両方のディスパッチがメモリの同じ領域に UAV の書き込みを実行すると、2 つの隣接ディスパッチの並列実行が原因で競合状態が発生する可能性があります。
UAV バリアにより、後続のディスパッチが開始される前に、以前送信されたすべてのディスパッチが GPU で強制的に実行されます。 UAV バリアは、同じコマンド リストのディスパッチ間で同期を行ってデータの競合を回避するために使用されます。 ID3D12GraphicsCommandList::ResourceBarrier メソッドを使用して UAV バリアを発行できます。
DirectML での UAV バリア
DirectML では、Direct3D 12 で計算シェーダーがディスパッチされるのと同じ方法で演算子がディスパッチされます。 つまり、演算子の隣接ディスパッチ間に介在する UAV バリアが存在する場合を除いて、演算子の隣接ディスパッチは並列に実行することが許可されます。 一般的な機械学習モデルでは、その演算子間にデータ依存関係が含まれます。たとえば、一方の演算子の出力が他方の演算子の入力に取り込まれます。 したがって、ディスパッチを正しく同期するために、UAV バリアを使用することが重要です。
DirectML では、入力テンソルからの読み取りのみを行うことが保証されます (書き込みは行われません)。 また、テンソルの DML_BUFFER_TENSOR_DESC::TotalTensorSizeInBytes メンバーの範囲外の出力テンソルへの書き込みは一切行われないことも保証されます。 したがって、DirectML での演算子間のデータ依存関係は、演算子の入力と出力のバインディングを確認するだけで判断できます。
こうした保証によって、たとえば、介在する UAV バリアを発行することなく、リソースの同じ領域をバインドする 2 つの演算子を入力としてディスパッチできます。 DirectML は入力テンソルに書き込むことはないため、これは常に安全です。 別の例として、同時に実行される 2 つの演算子ディスパッチの出力テンソルを同じ Direct3D 12 リソースにバインドすることも (それらのテンソルが重複しない限り) 常に安全です。これは、DirectML が、(テンソルの DML_BUFFER_TENSOR_DESC::TotalTensorSizeInBytes で定義されているように) テンソルのバインドの外部には書き込みを行わないためです。
UAV バリアは一種の同期であるため、UAV バリアを不必要に使用すると、パフォーマンスが低下する可能性があります。 したがって、コマンド リスト内のディスパッチを正しく同期するために必要な最小数の UAV バリアを使用するのが最適です。
例 1
次の例では、コンボリューション演算子の出力が ReLU アクティベーションに取り込まれ、その後、バッチ正規化が行われます。
CONVOLUTION (conv1)
|
ACTIVATION_RELU (relu1)
|
BATCH_NORMALIZATION (batch1)
3 つすべての演算子の間にデータ依存関係が存在するため、それぞれの連続するディスパッチ間で UAV バリアが必要です (IDMLCommandRecorder::RecordDispatch に関する記事を参照してください)。
dmlCommandRecorder->RecordDispatch(d3d12CommandList,
conv1)
d3d12CommandList->ResourceBarrier(
UAV バリア)
dmlCommandRecorder->RecordDispatch(d3d12CommandList,
relu1)
d3d12CommandList->ResourceBarrier(
UAV バリア)
dmlCommandRecorder->RecordDispatch(d3d12CommandList,
batch1)
例 2
MAX_POOLING (pool1)
/ \
CONVOLUTION CONVOLUTION
(conv1) (conv2)
\ /
JOIN (join1)
以下では、プーリングの出力が 2 つのコンボリューションに取り込まれ、次にその出力が JOIN 演算子を使用して連結されます。 pool1
と conv1
および conv2
の両方の間にデータ依存関係が存在し、また conv1
および conv2
の両方と join1
の間にもデータ依存関係が存在します。 このグラフを実行する 1 つの有効な方法を次に示します。
dmlCommandRecorder->RecordDispatch(d3d12CommandList,
pool1)
d3d12CommandList->ResourceBarrier(
UAV バリア)
dmlCommandRecorder->RecordDispatch(d3d12CommandList,
conv1)
dmlCommandRecorder->RecordDispatch(d3d12CommandList,
conv2)
d3d12CommandList->ResourceBarrier(
UAV バリア)
dmlCommandRecorder->RecordDispatch(d3d12CommandList,
join1)
この場合、conv1
と conv2
を GPU で同時に実行でき、パフォーマンスを向上させることができます。
リソース バリア状態の要件
GPU で DirectML ディスパッチを実行する前にすべての Direct3D 12 リソースを正しいリソース バリア状態にするのは、呼び出し元としてのユーザーの責任です。 DirectML は、ユーザーの代わりに移行のバリアを実行しません。
GPU で IDMLCommandRecorder::RecordDispatch を実行する前に、バインドされているすべてのリソースを D3D12_RESOURCE_STATE_UNORDERED_ACCESS 状態、または暗黙的に D3D12_RESOURCE_STATE_UNORDERED_ACCESS に昇格できる状態 (D3D12_RESOURCE_STATE_COMMON など) に移行する必要があります。 この呼び出しが完了した後も、リソースは D3D12_RESOURCE_STATE_UNORDERED_ACCESS 状態のままです。 詳細については、「DirectML でのバインド」を参照してください。