UAV-Barrieren und Ressourcenzustandsbarrieren in DirectML

Anforderungen für unsortierte Zugriffsansicht-Barrieren (Unordered Access View, UAV)

UAV-Barrieren in Direct3D 12

In Direct3D 12 dürfen benachbarte Compute-Shader-Dispatcher innerhalb derselben Befehlsliste parallel auf der GPU ausgeführt werden, es sei denn, sie werden mit einer dazwischen liegenden unsortierten Zugriffsansicht-Barriere (UAV) synchronisiert. Dies kann die Leistung verbessern, indem die Auslastung der GPU-Hardware erhöht wird. Die parallele Ausführung zweier angrenzender Dispatcher kann jedoch standardmäßig ohne die Verwendung einer UAV-Barriere zu einer Racebedingung führen, wenn eine Datenabhängigkeit zwischen den beiden Verteilern besteht; oder wenn beide Dispatcher UAV-Schreibvorgänge in die gleichen Speicherbereiche ausführen.

Eine UAV-Barriere erzwingt alle zuvor übermittelten Dispatches, um die GPU abzuschließen, bevor nachfolgende Dispatches beginnen können. UAV-Barrieren werden verwendet, um zwischen Dispatches in derselben Befehlsliste zu synchronisieren, um Datenraces zu vermeiden. Sie können eine UAV-Barriere mithilfe der ID3D12GraphicsCommandList::ResourceBarrier-Methode ausstellen.

UAV-Barrieren in DirectML

In DirectML werden Operatoren auf eine Weise verteilt, die der Art und Weise ähnelt, wie Compute-Shader in Direct3D 12 verteilt werden. Das heißt, benachbarte Dispatches von Operatoren dürfen parallel auf der GPU ausgeführt werden, es sei denn, es gibt eine dazwischen liegende UAV-Barriere. Ein typisches Modell für maschinelles Lernen enthält Datenabhängigkeiten zwischen seinen Operatoren; so fließt beispielsweise die Ausgabe eines Operators in die Eingabe eines anderen ein. Daher ist es wichtig, UAV-Barrieren zu verwenden, um Dispatches korrekt zu synchronisieren.

DirectML garantiert, dass sie nur von Eingabe-Tensoren gelesen (und darin) geschrieben wird. Es garantiert auch, dass es niemals Schreibvorgänge in einen Ausgabe-Tensor außerhalb des Bereichs des DML_BUFFER_TENSOR_DESC::TotalTensorSizeInBytes herstellt. Dies bedeutet, dass Datenabhängigkeiten zwischen Operatoren in DirectML begründet werden können, indem nur die Eingabe- und Ausgabebindungen eines Operators betrachtet werden.

Mit diesen Garantien können Sie beispielsweise zwei Operatoren versenden, die dieselbe Region einer Ressource als Eingabe binden, ohne eine dazwischen liegende UAV-Barriere ausstellen zu müssen. Dies ist immer sicher, da DirectML nie in Eingabe-Tensoren schreibt. Ein weiteres Beispiel ist es immer sicher, die Ausgabe-Tensoren von zwei gleichzeitigen Operatoren an dieselbe Direct3D 12-Ressource zu binden (solange sich ihre Tensoren nicht überlappen), da DirectML niemals außerhalb der Grenzen eines Tensors schreibt (wie durch die DML_BUFFER_TENSOR_DESC::TotalTensorSizeInBytes definiert).

Da UAV-Barrieren eine Form der Synchronisierung sind, kann sich die unnötige Verwendung von UAV-Barrieren negativ auf die Leistung auswirken. Daher ist es am besten, dass Sie die minimale Anzahl von UAV-Barrieren verwenden, die erforderlich sind, um die Verteiler innerhalb einer Befehlsliste ordnungsgemäß zu synchronisieren.

Beispiel 1

Im folgenden Beispiel wird die Ausgabe eines Konvolutions-Operators in eine ReLU-Aktivierung eingespeist, gefolgt von einer Batchnormalisierung.

    CONVOLUTION (conv1)
         |
  ACTIVATION_RELU (relu1)
         |
BATCH_NORMALIZATION (batch1)

Da eine Datenabhängigkeit zwischen allen drei Operatoren vorhanden ist, benötigen Sie eine UAV-Barriere zwischen jedem aufeinander folgenden Dispatch (siehe IDMLCommandRecorder::RecordDispatch).

  1. dmlCommandRecorder->RecordDispatch(d3d12CommandList, conv1)
  2. d3d12CommandList->ResourceBarrier(UAV-Barriere)
  3. dmlCommandRecorder->RecordDispatch(d3d12CommandList, relu1)
  4. d3d12CommandList->ResourceBarrier(UAV-Barriere)
  5. dmlCommandRecorder->RecordDispatch(d3d12CommandList, batch1)

Beispiel 2

     MAX_POOLING (pool1)
        /    \
CONVOLUTION  CONVOLUTION
  (conv1)      (conv2)
        \    /
         JOIN (join1)

Hier wird die Ausgabe der Poolerstellung in zwei Konvolutionen eingespeist, deren Ausgaben dann mithilfe des JOIN-Operators verkettet werden. Eine Datenabhängigkeit besteht zwischen pool1 und sowohl conv1 und conv2 als auch zwischen beiden conv1 und conv2 und join1. Hier ist eine gültige Methode zum Ausführen dieses Diagramms.

  1. dmlCommandRecorder->RecordDispatch(d3d12CommandList, pool1)
  2. d3d12CommandList->ResourceBarrier(UAV-Barriere)
  3. dmlCommandRecorder->RecordDispatch(d3d12CommandList, conv1)
  4. dmlCommandRecorder->RecordDispatch(d3d12CommandList, conv2)
  5. d3d12CommandList->ResourceBarrier(UAV-Barriere)
  6. dmlCommandRecorder->RecordDispatch(d3d12CommandList, join1)

In diesem Fall conv1 können sie conv2 gleichzeitig auf der GPU ausgeführt werden, wodurch die Leistung verbessert werden kann.

Anforderungen für den Zustand von Ressourcenbarrieren

Als aufrufende Funktion liegt es in Ihrer Zuständigkeit, sicherzustellen, dass sich alle Direct3D 12-Ressourcen vor dem Ausführen von DirectML-Verteilern auf der GPU im richtigen Zustand der Ressourcenbarriere befinden. DirectML führt keine Übergangsbarrieren in Ihrem Auftrag durch.

Vor der Ausführung von IDMLCommandRecorder::RecordDispatch auf der GPU müssen Sie alle gebundenen Ressourcen in den D3D12_RESOURCE_STATE_UNORDERED_ACCESS-Zustand oder auf einen Zustand übertragen, der implizit auf D3D12_RESOURCE_STATE_UNORDERED_ACCESS verfügbar ist, z . B. D3D12_RESOURCE_STATE_COMMON. Nach Abschluss dieses Aufrufs bleiben die Ressourcen im D3D12_RESOURCE_STATE_UNORDERED_ACCESS-Zustand. Weitere Informationen finden Sie unter Binding in DirectML.

Weitere Informationen