Partager via


Barrières UAV et barrières d’état des ressources dans DirectML

Exigences relatives aux barrières de type vue d’accès non ordonnée (UAV)

Barrières de l’UAV dans Direct3D 12

Dans Direct3D 12, les lancements consécutifs de nuanceurs de calcul au sein d’une même liste de commandes peuvent s’exécuter en parallèle sur le GPU, sauf s’ils sont synchronisés par une barrière UAV (vue d’accès non ordonné) intermédiaire. Cela peut améliorer les performances en augmentant l’utilisation du matériel GPU. Toutefois, par défaut, sans l’utilisation d’une barrière UAV, l’exécution parallèle de deux dispatches adjacents peut déclencher une condition de concurrence s’il existe une dépendance de données entre les deux dispatches ; ou si les deux dispatches effectuent des écritures UAV dans les mêmes régions de mémoire.

Une barrière UAV force toutes les commandes envoyées précédemment à terminer l'exécution sur le GPU avant que les commandes ultérieures puissent commencer. Les barrières UAV sont utilisées pour synchroniser les lancements au sein d’une même liste de commandes afin d’éviter les conflits d’accès aux données. Vous pouvez émettre une barrière UAV à l’aide de la méthode ID3D12GraphicsCommandList ::ResourceBarrier.

Barrières de l’UAV dans DirectML

Dans DirectML, les opérateurs sont distribués de manière similaire à la façon dont les nuanceurs de calcul sont distribués dans Direct3D 12. Autrement dit, les envois adjacents d’opérateurs sont autorisés à s’exécuter en parallèle sur le GPU, sauf s'il existe une barrière UAV intermédiaire entre eux. Un modèle Machine Learning classique contient des dépendances de données entre ses opérateurs ; par exemple, la sortie d’un opérateur alimente l’entrée d’une autre. Il est donc important d’utiliser des barrières UAV pour synchroniser correctement les répartitions.

DirectML garantit qu'il lira uniquement à partir des tenseurs d'entrée et n'écrira jamais dans ceux-ci. Il garantit également qu'il n'effectuera jamais d'écritures dans un tenseur de sortie en dehors des bornes définies par le membre DML_BUFFER_TENSOR_DESC::TotalTensorSizeInBytes. Cela signifie que les dépendances de données entre les opérateurs dans DirectML peuvent être raisonnée en examinant uniquement les liaisons d’entrée et de sortie d’un opérateur.

Par exemple, ces garanties vous permettent d'envoyer deux opérateurs qui lient la même région d'une ressource en tant qu'entrée, sans avoir à émettre une barrière UAV intermédiaire. Cela est toujours sûr, car DirectML n’écrit jamais dans les tenseurs d’entrée. Autre exemple : il est toujours sûr de lier les tenseurs de sortie de deux lancements d’opérateurs simultanés à la même ressource Direct3D 12 (tant que leurs tenseurs ne se chevauchent pas), car DirectML n’écrit jamais en dehors des limites d’un tenseur (telles que définies par le champ DML_BUFFER_TENSOR_DESC::TotalTensorSizeInBytes).

Étant donné que les barrières UAV constituent une forme de synchronisation, l’utilisation inutile des barrières UAV peut avoir un impact négatif sur les performances. Par conséquent, il est préférable d’utiliser le nombre minimal de barrières UAV nécessaires pour synchroniser correctement les dispatches au sein d'une liste de commandes.

Exemple 1

Dans l’exemple suivant, la sortie d’un opérateur de convolution est transmise à une activation ReLU, suivie d’une normalisation par lots.

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

Étant donné qu’une dépendance de données existe entre les trois opérateurs, vous aurez besoin d’une barrière UAV entre chaque répartition successive (voir IDMLCommandRecorder ::RecordDispatch).

  1. dmlCommandRecorder->RecordDispatch(d3d12CommandList, conv1)
  2. d3d12CommandList->ResourceBarrier( Barrière de l’UAV)
  3. dmlCommandRecorder->RecordDispatch(d3d12CommandList, relu1)
  4. d3d12CommandList->ResourceBarrier( Barrière de l’UAV)
  5. dmlCommandRecorder->RecordDispatch(d3d12CommandList, Lot 1)

Exemple 2

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

Ici, la sortie du pooling est transférée en deux convolutions, dont les sorties sont ensuite concaténées ensemble à l’aide de l’opérateur JOIN. Une dépendance de données existe entre pool1 et les deux conv1 et conv2; ainsi qu’entre les deux conv1 et conv2 .join1 Voici un moyen valide d’exécuter ce graphique.

  1. dmlCommandRecorder->RecordDispatch(d3d12CommandList, Piscine1)
  2. d3d12CommandList->ResourceBarrier( Barrière de l’UAV)
  3. dmlCommandRecorder->RecordDispatch(d3d12CommandList, conv1)
  4. dmlCommandRecorder->RecordDispatch(d3d12CommandList, conv2)
  5. d3d12CommandList->ResourceBarrier( Barrière de l’UAV)
  6. dmlCommandRecorder->RecordDispatch(d3d12CommandList, Rejoindre1)

Dans ce cas, conv1 et conv2 peuvent s’exécuter simultanément sur le GPU, ce qui peut améliorer les performances.

Exigences relatives à l’état de la barrière des ressources

En tant qu’appelant, il est de votre responsabilité de s’assurer que toutes les ressources Direct3D 12 sont dans l’état de barrière de ressource approprié avant d’exécuter des distributions DirectML sur le GPU. DirectML n’effectue aucune barrière de transition en votre nom.

Avant l’exécution de IDMLCommandRecorder ::RecordDispatch sur le GPU, vous devez passer toutes les ressources liées à l’état D3D12_RESOURCE_STATE_UNORDERED_ACCESS , ou à un état implicitement promotionnel à D3D12_RESOURCE_STATE_UNORDERED_ACCESS, comme D3D12_RESOURCE_STATE_COMMON. Une fois cet appel terminé, les ressources restent dans l’état D3D12_RESOURCE_STATE_UNORDERED_ACCESS . Pour plus d’informations, consultez Liaison dans DirectML.

Voir aussi