Compartilhar via


Usando passos para expressar o preenchimento e o layout de memória

Tensores DirectML, que são apoiados por buffers Direct3D 12, são descritos por propriedades conhecidas como os tamanhos e os passos do tensor. Os tamanhos do tensor descrevem as dimensões lógicas do tensor. Por exemplo, um tensor 2D pode ter uma altura de 2 e uma largura de 3. Logicamente, o tensor tem seis elementos distintos, embora os tamanhos não especifiquem como esses elementos são armazenados na memória. Os passos do tensor descrevem o layout de memória física dos elementos do tensor.

Matrizes bidimensionais (2D)

Considere um tensor 2D que tenha uma altura de 2 e uma largura de 3; os dados compreendem caracteres textuais. Em C/C++, isso pode ser expresso usando uma matriz multidimensional.

constexpr int rows = 2;
constexpr int columns = 3;
char tensor[rows][columns];
tensor[0][0] = 'A';
tensor[0][1] = 'B';
tensor[0][2] = 'C';
tensor[1][0] = 'D';
tensor[1][1] = 'E';
tensor[1][2] = 'F';

A exibição lógica do tensor acima é visualizada abaixo.

A B C
D E F

Em C/C++, uma matriz multidimensional é armazenada em ordem principal de linha. Em outras palavras, os elementos consecutivos ao longo da dimensão de largura são armazenados contíguamente no espaço de memória linear.

Deslocamento: 0 1 2 3 4 5
Valor: Um B C D E F

O passo de uma dimensão é o número de elementos a serem ignoradas para acessar o próximo elemento nessa dimensão. Os passos expressam o layout do tensor na memória. Com uma ordem principal de linha, o passo da dimensão de largura é sempre 1, uma vez que os elementos adjacentes ao longo da dimensão são armazenados contíguamente. O passo da dimensão de altura depende do tamanho da dimensão de largura; no exemplo acima, a distância entre elementos consecutivos ao longo da dimensão de altura (por exemplo, A a D) é igual à largura do tensor (que é 3 neste exemplo).

Para ilustrar um layout diferente, considere a ordem principal da coluna. Em outras palavras, os elementos consecutivos ao longo da dimensão de altura são armazenados contíguamente no espaço de memória linear. Nesse caso, o passo de altura é sempre 1 e o passo de largura é 2 (o tamanho da dimensão de altura).

Deslocamento: 0 1 2 3 4 5
Valor: Um D B E C F

Dimensões mais altas

Quando se trata de mais de duas dimensões, é difícil se referir a um layout como linha principal ou coluna-principal. Portanto, o restante deste tópico usa termos e rótulos como esses.

  • 2D: "HW"— a altura é a dimensão de ordem mais alta (linha principal).
  • 2D: "WH"— a largura é a dimensão de ordem mais alta (coluna-principal).
  • 3D: "DHW"— a profundidade é a dimensão de ordem mais alta, seguida pela altura e, em seguida, pela largura.
  • 3D: "WHD"— a largura é a dimensão de ordem mais alta, seguida pela altura e, em seguida, pela profundidade.
  • 4D: "NCHW": o número de imagens (tamanho do lote), o número de canais, a altura e a largura.

Em geral, o passo empacotado de uma dimensão é igual ao produto dos tamanhos das dimensões de ordem inferior. Por exemplo, com um layout "DHW", o passo D é igual a H * W; o passo H é igual a W; e o passo W é igual a 1. Dizem que os passos são empacotados quando o tamanho físico total do tensor é igual ao tamanho lógico total do tensor; em outras palavras, não há espaço extra nem elementos sobrepostos.

Vamos estender o exemplo 2D para três dimensões, de modo que tenhamos um tensor com profundidade 2, altura 2 e largura 3 (para um total de 12 elementos lógicos).

A B C
D E F

G H I
J K L

Com um layout "DHW", esse tensor é armazenado da seguinte maneira.

Deslocamento: 0 1 2 3 4 5 6 7 oito 9 10 11
Valor: Um B C D E F G H /I J K L
  • D-stride = altura (2) * largura (3) = 6 (por exemplo, a distância entre 'A' e 'G').
  • H-stride = largura (3) = 3 (por exemplo, a distância entre 'A' e 'D').
  • W-stride = 1 (por exemplo, a distância entre 'A' e 'B').

O produto ponto dos índices/coordenadas de um elemento e os passos fornece o deslocamento para esse elemento no buffer. Por exemplo, o deslocamento do elemento H (d=1, h=0, w=1) é 7.

{1, 0, 1} ⋅ {6, 3, 1} = 1 * 6 + 0 * 3 + 1 * 1 = 7

Tensores empacotados

Os exemplos acima ilustram tensores empacotados . Diz-se que um tensor é empacotado quando o tamanho lógico do tensor (em elementos) é igual ao tamanho físico do buffer (em elementos) e cada elemento tem um endereço/deslocamento exclusivo. Por exemplo, um tensor 2x2x3 será empacotado se o buffer tiver 12 elementos de comprimento e nenhum par de elementos compartilhar o mesmo deslocamento no buffer. Tensores empacotados são o caso mais comum; mas os passos permitem layouts de memória mais complexos.

Difusão com passos

Se o tamanho do buffer de um tensor (em elementos) for menor que o produto de suas dimensões lógicas, deve haver sobreposição de elementos. O caso usual para isso é conhecido como difusão; em que os elementos de uma dimensão são uma duplicata de outra dimensão. Por exemplo, vamos revisitar o exemplo 2D. Digamos que queremos um tensor que seja logicamente 2x3, mas a segunda linha é idêntica à primeira linha. É assim que isso se apresenta.

A B C
A B C

Isso pode ser armazenado como um tensor HW/row-major empacotado. Mas um armazenamento mais compacto conteria apenas 3 elementos (A, B e C) e usaria um passo de altura de 0 em vez de 3. Nesse caso, o tamanho físico do tensor é de 3 elementos, mas o tamanho lógico é de 6 elementos.

Em geral, se o passo de uma dimensão for 0, todos os elementos nas dimensões de ordem inferior serão repetidos ao longo da dimensão difundida; por exemplo, se o tensor for NCHW e o passo C for 0, cada canal terá os mesmos valores ao longo de H e W.

Preenchimento com passos

Diz-se que um tensor será acolchoado se seu tamanho físico for maior do que o tamanho mínimo necessário para ajustar seus elementos. Quando não há elementos de difusão nem sobreposição, o tamanho mínimo do tensor (em elementos) é simplesmente o produto de suas dimensões. Você pode usar a função DMLCalcBufferTensorSize auxiliar (consulte as funções auxiliares do DirectML para uma listagem dessa função) para calcular o tamanho mínimo do buffer para seus tensores DirectML.

Digamos que um buffer contenha os seguintes valores (os elementos 'x' indicam valores de preenchimento).

0 1 2 3 4 5 6 7 oito 9
Um B C x x D E F x x

O tensor acolchoados pode ser descrito usando um passo de altura de 5 em vez de 3. Em vez de passar por três elementos para chegar à próxima linha, a etapa é de 5 elementos (3 elementos reais mais 2 elementos de preenchimento). O preenchimento é comum em elementos gráficos de computação, por exemplo, para garantir que uma imagem tenha um alinhamento de potência de dois.

A B C
D E F

Descrições de tensores de buffer do DirectML

O DirectML pode funcionar com uma variedade de disposições físicas de tensores, já que a estrutura DML_BUFFER_TENSOR_DESC tem membros Sizes e Strides. Algumas implementações de operador podem ser mais eficientes com um layout específico, portanto, não é incomum alterar a forma como os dados tensor são armazenados para melhorar o desempenho.

A maioria dos operadores DirectML exige tensores 4D ou 5D e a ordem dos valores de tamanhos e passos é fixada. Corrigindo a ordem dos tamanhos e valores de passo em uma descrição tensor, é possível que o DirectML infera layouts físicos diferentes.

4D

5D

  • DML_BUFFER_TENSOR_DESC::Tamanhos = { Tamanho N, Tamanho C, Tamanho D, Tamanho H, Tamanho W }
  • DML_BUFFER_TENSOR_DESC::Passos = { Passo N, Passo C, Passo D, Passo H, Passo W }

Se um operador DirectML exigir um tensor 4D ou 5D, mas os dados reais tiverem uma classificação menor (por exemplo, 2D), as dimensões principais deverão ser preenchidas com 1s. Por exemplo, um tensor "HW" é definido usando DML_BUFFER_TENSOR_DESC::Sizes = { 1, 1, H, W }.

Se os dados tensores forem armazenados em NCHW/NCDHW, não será necessário definir DML_BUFFER_TENSOR_DESC::Strides, a menos que você queira transmitir ou preencher. Você pode definir o campo passo a passo como nullptr. No entanto, se os dados tensores forem armazenados em outro layout, como o NHWC, você precisará de avanços para expressar a transformação de NCHW para esse layout.

Para um exemplo simples, considere a descrição de um tensor 2D com altura 3 e largura 5.

NCHW empacotado (passos implícitos)

  • DML_BUFFER_TENSOR_DESC::Tamanhos = { 1, 1, 3, 5 }
  • DML_BUFFER_TENSOR_DESC::Passos = nullptr

NCHW empacotado (avanços explícitos)

  • Passada N = tamanho C * tamanho H * tamanho W = 1 * 3 * 5 = 15
  • C-passo = Altura * Largura = 3 * 5 = 15
  • Passada H = tamanho W = 5
  • W-passo = 1
  • DML_BUFFER_TENSOR_DESC::Tamanhos = { 1, 1, 3, 5 }
  • DML_BUFFER_TENSOR_DESC::Passos = { 15, 15, 5, 1 }

NHWC empacotado

  • N-stride = H-size * W-size * C-size = 3 vezes 5 vezes 1 = 15
  • Passada H = tamanho W * tamanho C = 5 * 1 = 5
  • W-stride = C-size = 1
  • Passada em C = 1
  • DML_BUFFER_TENSOR_DESC::Tamanhos = { 1, 1, 3, 5 }
  • DML_BUFFER_TENSOR_DESC::Passos = { 15, 1, 5, 1 }

Consulte também