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

Conditions des barrières de vue d’accès non ordonnée (UAV)

Barrières UAV dans Direct3D 12

Dans Direct3D 12, les répartitions de nuanceurs de calcul adjacents au sein la même liste de commandes sont autorisées à s’exécuter en parallèle sur le GPU, sauf si elles sont synchronisées avec une barrière de vue d’accès non ordonnée (UAV) 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 répartitions adjacentes peut entraîner une condition de concurrence s’il existe une dépendance de données entre les deux répartitions, ou si les deux répartitions effectuent des écritures UAV dans les mêmes régions de mémoire.

Une barrière UAV force toutes les distributions envoyées précédemment à se terminer l’exécution sur le GPU avant que les répartitions suivantes puissent commencer. Les barrières UAV sont utilisées pour synchroniser les répartitions sur la même liste de commandes afin d’éviter les concurrences de données. Vous pouvez émettre une barrière UAV à l’aide de la méthode ID3D12GraphicsCommandList::ResourceBarrier.

Barrières UAV dans DirectML

Dans DirectML, les opérateurs sont répartis de manière similaire à la manière dont les nuanceurs de calcul sont répartis dans Direct3D 12. Autrement dit, les répartitions adjacentes d’opérateurs sont autorisées à 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 standard 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 n’est jamais lu (ou écrit) à partir des tenseurs d’entrée. Il garantit également qu’il ne fabriquera jamais d’écritures dans un tenseur de sortie en dehors de la plage du membre DML_BUFFER_TENSOR_DESC::TotalTensorSizeInBytes du tenseur. 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 de répartir deux opérateurs qui lient la même région d’une ressource qu’une entrée, sans avoir à émettre une barrière DAV intermédiaire. Cela est toujours sécurisé, car DirectML n’écrit jamais dans les tenseurs d’entrée. Comme autre exemple, il est toujours sécurisé de lier les tenseurs de sortie de deux 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 (tel que défini par le DML_BUFFER_TENSOR_DESC::TotalTensorSizeInBytes du tenseur).

Étant donné que les barrières UAV constituent une forme de synchronisation, l’utilisation non nécessaire 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 répartitions dans 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 UAV)
  3. dmlCommandRecorder->RecordDispatch(d3d12CommandList, relu1)
  4. d3d12CommandList->ResourceBarrier(Barrière UAV)
  5. dmlCommandRecorder->RecordDispatch(d3d12CommandList, batch1)

Exemple 2

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

Ici, la sortie du regroupement alimente deux convolutions, dont les sorties sont ensuite concaténées à l’aide de l’opérateur de jointure. Une dépendance de données existe entre pool1, et conv1 et conv2, ainsi qu’entre les conv1 et conv2, et join1. Voici un moyen valide d’exécuter ce graphe.

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

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 vous 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 faire passer toutes les ressources liées à l’état D3D12_RESOURCE_STATE_UNORDERED_ACCESS, ou à un état pouvant être implicitement promu à 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