Barreiras de UAV e barreiras de estado de recursos no DirectML

Requisitos de barreira de visualização de acesso não ordenado (UAV)

Barreiras de UAV no Direct3D 12

No Direct3D 12, os despachos de 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 visualizaçã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 UAV, a execução paralela de dois despachos adjacentes pode causar uma condição de corrida se existir uma dependência de dados entre os dois despachos; ou se ambos os despachos executarem gravações UAV nas mesmas regiões de memória.

Uma barreira de UAV obriga todos os despachos previamente submetidos a concluírem a execução na GPU antes que os despachos subsequentes possam começar. As barreiras de UAV são usadas para sincronizar entre despachos na mesma lista de comandos para evitar corridas de dados. Você pode emitir uma barreira UAV usando o método ID3D12GraphicsCommandList::ResourceBarrier.

Barreiras de UAV no DirectML

No DirectML, os operadores são despachados de forma semelhante à forma como os shaders de computação são despachados no Direct3D 12. Ou seja, despachos adjacentes de operadores podem ser executados 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 os despachos.

DirectML garante que apenas lerá (e nunca gravará em) tensores de entrada. Ele também garante que nunca executará operações de escrita em um tensor de saída além dos limites do membro DML_BUFFER_TENSOR_DESC::TotalTensorSizeInBytes do tensor. Isso significa que as dependências de dados entre operadores no DirectML podem ser raciocinadas examinando apenas as ligações de entrada e saída de um operador.

Por exemplo, essas garantias permitem que você despache 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 despachos de operadores simultâneos ao mesmo recurso Direct3D 12 (desde que seus tensores não se sobreponham), porque o DirectML nunca escreve fora dos limites de um tensor, conforme definido pela propriedade TotalTensorSizeInBytes do DML_BUFFER_TENSOR_DESC do tensor.

Como as barreiras de UAV são uma forma de sincronização, o uso desnecessário de barreiras de UAV pode afetar negativamente o desempenho. Portanto, recomenda-se usar o número mínimo de barreiras de UAV necessárias para sincronizar corretamente os comandos numa lista de comandos.

Exemplo 1

No exemplo a seguir, a saída de um operador de convolução é alimentada numa ativação ReLU, seguida por uma normalização por 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 despacho sucessivo (consulte IDMLCommandRecorder::RecordDispatch).

  1. dmlCommandRecorder->RecordDispatch(d3d12CommandList, Conv1)
  2. d3d12CommandList->ResourceBarrier( Barreira UAV)
  3. dmlCommandRecorder->RecordDispatch(d3d12CommandList, RELU1)
  4. d3d12CommandList->ResourceBarrier( Barreira UAV)
  5. dmlCommandRecorder->RecordDispatch(d3d12CommandList, lote 1)

Exemplo 2

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

Aqui, a saída do pooling é 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 ambos conv1 e conv2; bem como entre ambos conv1 e conv2 e join1. Aqui está uma maneira válida de executar este gráfico.

  1. dmlCommandRecorder->RecordDispatch(d3d12CommandList, piscina1)
  2. d3d12CommandList->ResourceBarrier( Barreira UAV)
  3. dmlCommandRecorder->RecordDispatch(d3d12CommandList, Conv1)
  4. dmlCommandRecorder->RecordDispatch(d3d12CommandList, Conv2)
  5. d3d12CommandList->ResourceBarrier( Barreira UAV)
  6. dmlCommandRecorder->RecordDispatch(d3d12CommandList, juntar-se1)

Neste caso, conv1 e conv2 são capazes de executar simultaneamente na GPU, o que pode melhorar o desempenho.

Requisitos de estado de barreira de recursos

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

Antes da execução de IDMLCommandRecorder::RecordDispatch na GPU, você deve fazer a transição de todos os recursos vinculados 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 Vinculação em DirectML.

Ver também