Использование шагов для выражения заполнения и макета памяти

Тензоры DirectML , которые поддерживаются буферами Direct3D 12, описываются свойствами, известными как размеры и шаги тензора. Размеры тензора описывают логические измерения тензора. Например, 2D-тензор может иметь высоту 2 и ширину 3. Логически, тензор имеет 6 отдельных элементов, хотя размеры не указывают, как эти элементы хранятся в памяти. Шаги тензора определяют физическое расположение памяти элементов тензора.

Двухмерные массивы (2D)

Рассмотрим тензор 2D, имеющий высоту 2 и ширину 3; Данные состоят из текстовых символов. В C/C++это может быть выражено с помощью многомерного массива.

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 B C
D E F

В C/C++многомерный массив хранится в основном порядке строк. Другими словами, последовательные элементы вдоль измерения ширины хранятся в линейном пространстве памяти.

Смещение: 0 1 2 3 4 5
Значение: А Б С Д Е Ф

Шаг измерения — это количество элементов, которые нужно пропустить, чтобы получить доступ к следующему элементу в этом измерении. Шаги выражают структуру тензора в памяти. При наличии основного порядка строк шаг измерения ширины всегда равен 1, так как смежные элементы вдоль измерения хранятся последовательно. Шаг измерения высоты зависит от размера измерения ширины; в приведенном выше примере расстояние между последовательными элементами по измерению высоты (например, A to D) равно ширине тензора (в этом примере — 3).

Чтобы проиллюстрировать другой макет, рассмотрим порядок столбцов. Другими словами, последовательные элементы вдоль измерения высоты хранятся в линейном пространстве памяти. В этом случае высота шага всегда равна 1, а ширина шага — 2 (размер измерения высоты).

Смещение: 0 1 2 3 4 5
Значение: А Д Б Е С Ф

Более высокие размеры

Когда речь идет о более чем двух измерениях, трудно говорить о структуре данных как о строко-ориентированной или столбцово-ориентированной. Поэтому в остальной части этого раздела используются термины и метки, такие как эти.

  • 2D: "HW"—высота — это размерность наивысшего порядка (по строкам).
  • 2D: "WH" — ширина — это измерение верхнего порядка (столбец-основной).
  • 3D: "DHW" — глубина — это измерение верхнего порядка, за которым следует высота, а затем ширина.
  • 3D: "WHD", ширина — это измерение верхнего порядка, за которым следует высота, а затем глубина.
  • 4D: "NCHW" — количество изображений (размер пакета), а затем количество каналов, а затем высота, а затем ширина.

Как правило, упакованный шаг размерности равен произведению размеров размерностей низшего порядка. Например, со структурой DHW, D-шаг равен H * W; шаг H равен W; и шаг W равен 1. Говорят, шаги считаются оптимальными когда общий физический размер тензора равен общему логическому размеру тензора; другими словами, не существует дополнительного пространства и перекрывающихся элементов.

Давайте расширим 2D-пример до трех измерений, чтобы у нас был тензор с глубиной 2, высотой 2 и шириной 3 (в общей сложности 12 логических элементов).

A B C
D E F

G H I
J K L

В формате «DHW» этот тензор хранится следующим образом.

Смещение: 0 1 2 3 4 5 6 7 8 9 10 11
Значение: А Б С Д Е Ф Г Х Я J К L
  • D-stride = высота (2) * ширина (3) = 6 (например, расстояние между "A" и "G".
  • H-stride = ширина (3) = 3 (например, расстояние между "A" и "D").
  • W-stride = 1 (например, расстояние между "A" и "B".

Точка скалярного произведения индексов и координат элемента с шагами определяет смещение этого элемента в буфере. Например, смещение элемента H (d=1, h=0, w=1) равно 7.

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

Упакованные тензоры

Приведенные выше примеры иллюстрируют упакованные тензоры. Считается, что тензор упакован , если логический размер тензора (в элементах) равен физическому размеру буфера (в элементах), а каждый элемент имеет уникальный адрес или смещение. Например, тензор 2x2x3 считается компактным, если длина буфера составляет 12 элементов, и ни одна пара элементов не имеет одинакового отступа в буфере. Упакованные тензоры являются наиболее распространенным случаем; но шаги позволяют более сложные макеты памяти.

Трансляция с шагами

Если размер буфера тензора (в элементах) меньше, чем произведение его логических измерений, то следует, что должно быть некоторое перекрытие элементов. Данный случай обычно называется вещанием, когда элементы одного измерения дублируются в другом измерении. Например, давайте рассмотрим пример 2D. Предположим, что мы хотим тензор, имеющий логический размер 2x3, но вторая строка идентична первой. Вот как это выглядит.

A B C
A B C

Это может храниться в виде упакованного тензора HW/row-major. Но более компактное хранилище данных будет содержать только 3 элемента (A, B и C) и использовать высотный шаг 0 вместо 3. В этом случае физический размер тензора составляет 3 элемента, но логический размер составляет 6 элементов.

Как правило, если шаг измерения равен 0, все элементы в измерениях нижнего порядка повторяются вдоль широковещательного измерения; Например, если тензор имеет значение NCHW и C-шаг равен 0, то каждый канал имеет одинаковые значения вдоль H и W.

Заполнение с отслеживанием шагов

Тензор называется дополненным, если его физический размер больше минимального размера, необходимого для размещения всех его элементов. Если нет трансляции или перекрывающихся элементов, минимальный размер тензора (в элементах) просто является произведением его измерений. Вы можете использовать вспомогательные функции (см. вспомогательные DMLCalcBufferTensorSize для перечисления этой функции ) для вычисления минимального размера буфера для тензоров DirectML.

Предположим, что буфер содержит следующие значения (элементы x указывают значения заполнения).

0 1 2 3 4 5 6 7 8 9
А Б С x x Д Е Ф x x

Заполненный тензор можно описать, используя шаг высоты 5 вместо 3. Вместо перехода на 3 элемента, чтобы перейти к следующей строке, шаг составляет 5 элементов (3 реальных элемента и 2 заполняющих элемента). Заполнение обычно используется в компьютерной графике, например, чтобы убедиться, что изображение имеет двухуровневое выравнивание.

A B C
D E F

Описания тензора буфера DirectML

DirectML может работать с различными физическими тензорами, так как структура DML_BUFFER_TENSOR_DESC имеет оба Sizes элемента.Strides Некоторые реализации операторов могут быть более эффективными при определенном макете, поэтому часто изменяют способ хранения данных тензора для повышения производительности.

Большинству операторов DirectML требуются 4D или 5D-тензоры, а порядок размеров и значений шагов фиксирован. Исправив порядок размеров и значений шагов в описании тензора, DirectML может определить различные физические конфигурации.

4D

5D

  • DML_BUFFER_TENSOR_DESC::Размеры = { N-размер, C-размер, D-размер, H-размер, W-размер }
  • DML_BUFFER_TENSOR_DESC::Strides = { N-шаг, C-шаг, D-шаг, H-шаг, W-шаг }

Если функции DirectML требуется 4D или 5D тензор, но фактические данные имеют меньший ранг (например, 2D), необходимо заполнить ведущие измерения единицами. Например, тензор HW устанавливается с помощью DML_BUFFER_TENSOR_DESC::Sizes = { 1, 1, H, W }.

Если данные тензора хранятся в NCHW/NCDHW, то не нужно задавать DML_BUFFER_TENSOR_DESC::Strides, если только вам не нужен бродкастинг или пэддинг. Вы можете задать для поля шагов значение nullptr. Однако если данные тензора хранятся в другом макете, например NHWC, необходимо выполнить шаги, чтобы выразить преобразование из NCHW в этот макет.

Для простого примера рассмотрим описание тензора 2D с высотой 3 и шириной 5.

Упакованные NCHW (неявные шаги)

  • DML_BUFFER_TENSOR_DESC::Размеры = { 1, 1, 3, 5 }
  • DML_BUFFER_TENSOR_DESC::Шаги = nullptr

Упакованный NCHW (явные шаги)

  • N-шаг = C-размер * H-размер * W-размер = 1 * 3 * 5 = 15
  • C-шаг = H-размер * W-размер = 3 * 5 = 15
  • Шаг по высоте (H-stride) = Размер по ширине (W-size) = 5
  • W-образный шаг = 1
  • DML_BUFFER_TENSOR_DESC::Размеры = { 1, 1, 3, 5 }
  • DML_BUFFER_TENSOR_DESC::Strides = { 15, 15, 5, 1 }

Упакованный NHWC

  • N-шаг = H-размер * W-размер * C-размер = 3 * 5 * 1 = 15
  • H-стрид = W-размер * C-размер = 5 * 1 = 5
  • W-шаг = С-размер = 1
  • С-шаг = 1
  • DML_BUFFER_TENSOR_DESC::Размеры = { 1, 1, 3, 5 }
  • DML_BUFFER_TENSOR_DESC::Strides = { 15, 1, 5, 1 }

См. также