Barreiras de UAV e de estado do recurso no DirectML

Requisitos de barreira do Modo de Exibição de Acesso Não Ordenado (VANT)

Barreiras de UAV no Direct3D 12

No Direct3D 12, os dispatchables do sombreador de computação adjacentes dentro da mesma lista de comandos podem ser executados em paralelo na GPU, a menos que estejam sincronizados com uma barreira de exibição de acesso não ordenado (UAV) interveniente. Isso pode melhorar o desempenho aumentando a utilização do hardware da GPU. No entanto, por padrão, sem o uso de uma barreira de UAV, a execução paralela de duas expedições adjacentes pode causar uma condição de corrida se houver uma dependência de dados entre as duas expedições ou se ambas as expedições executarem gravações do UAV nas mesmas regiões da memória.

Uma barreira de UAV força todas as associações enviadas anteriormente a concluir a execução na GPU antes que as associações subsequentes possam começar. As barreiras de UAV são usadas para sincronizar entre expedições na mesma lista de comandos para evitar corridas de dados. Você pode emitir uma barreira de UAV usando o método ID3D12GraphicsCommandList::ResourceBarrier.

Barreiras de UAV no DirectML

No DirectML, os operadores são expedidos da mesma forma que os sombreadores de computação são expedidos no Direct3D 12. Ou seja, expedições adjacentes de operadores são autorizados a executar em paralelo na GPU, a menos que exista uma barreira de UAV interveniente entre eles. Um modelo típico de aprendizado de máquina contém dependências de dados entre seus operadores. Por exemplo, a saída de um operador alimenta a entrada de outro. Portanto, é importante usar barreiras de UAV para sincronizar corretamente as expedições.

O DirectML garante que só realizará leituras (e nunca gravações) nos tensores de entrada. Ele também garante que nunca fabricará gravações em um tensor de saída fora do intervalo do membro DML_BUFFER_TENSOR_DESC::TotalTensorSizeInBytes do tensor. Isso significa que as dependências de dados entre operadores no DirectML podem ser fundamentadas observando apenas as associações de entrada e saída de um operador.

Por exemplo, essas garantias permitem expedir dois operadores que associam a mesma região de um recurso como entrada, sem precisar emitir uma barreira de UAV intermediária. Isso é sempre seguro porque o DirectML nunca grava em tensores de entrada. Como outro exemplo, é sempre seguro associar os tensores de saída de dois dispatches de operadores simultâneos ao mesmo recurso Direct3D 12 (desde que seus tensores não se sobreponham), porque o DirectML nunca grava fora das associações de um tensor (conforme definido pelo tensor DML_BUFFER_TENSOR_DESC::TotalTensorSizeInBytesdo ).

Como as barreiras de VANT são uma forma de sincronização, o uso desnecessário de barreiras de VANT pode afetar negativamente o desempenho. Portanto, é melhor que você use o número mínimo de barreiras de UAV necessárias para sincronizar corretamente as expedições em uma lista de comandos.

Exemplo 1

No exemplo a seguir, a saída de um operador de convolução é alimentada em uma ativação ReLU, seguida por uma normalização em lote.

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

Como existe uma dependência de dados entre os três operadores, você precisará de uma barreira de UAV entre cada expedição sucessiva (consulte IDMLCommandRecorder::RecordDispatch).

  1. dmlCommandRecorder->RecordDispatch(d3d12CommandList, conv1)
  2. d3d12CommandList->ResourceBarrier(Barreira de UAV)
  3. dmlCommandRecorder->RecordDispatch(d3d12CommandList, relu1)
  4. d3d12CommandList->ResourceBarrier(Barreira de UAV)
  5. dmlCommandRecorder->RecordDispatch(d3d12CommandList, lote1)

Exemplo 2

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

Aqui, a saída do pool é alimentada em duas convoluções, cujas saídas são então concatenadas usando o operador JOIN. Existe uma dependência de dados entre pool1 e entre ambos os conv1 e conv2, bem como entre conv1, conv2 e join1. Aqui está uma maneira válida de executar esse gráfico.

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

Neste caso, conv1 e conv2 podem executar simultaneamente na GPU, o que pode melhorar o desempenho.

Requisitos do estado de barreira dos recursos

Como chamador, é sua responsabilidade garantir que todos os recursos do Direct3D 12 estejam no estado de barreira de recursos correto antes de fazer expedições DirectML na GPU. O DirectML não executa nenhuma barreira de transição em seu nome.

Antes da execução do IDMLCommandRecorder::RecordDispatch na GPU, faça a transição de todos os recursos associados para o estado D3D12_RESOURCE_STATE_UNORDERED_ACCESS ou para um estado implicitamente promocional para D3D12_RESOURCE_STATE_UNORDERED_ACCESS, como D3D12_RESOURCE_STATE_COMMON. Após a conclusão dessa chamada, os recursos permanecem no estado D3D12_RESOURCE_STATE_UNORDERED_ACCESS. Para obter mais detalhes, consulte Associação no DirectML.

Confira também