Udostępnij przez


Używanie przesunięć do wyrażenia wypełnienia i układu pamięci

Tensory DirectML, wspierane przez bufory Direct3D 12, są opisane przez właściwości znane jako rozmiary i przesunięcia tensorów. Rozmiary tensora opisują logiczne wymiary tensora. Na przykład tensor 2D może mieć wysokość 2 i szerokość 3. Logicznie tensor ma 6 odrębnych elementów, chociaż rozmiary nie określają sposobu przechowywania tych elementów w pamięci. Strides tensora określają fizyczny układ pamięciowy jego elementów.

Tablice dwuwymiarowe (2D)

Rozważ tensor 2D o wysokości 2 i szerokości 3; dane zawierają znaki tekstowe. W języku C/C++może to być wyrażone przy użyciu tablicy wielowymiarowej.

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';

Logiczny widok powyższego tensoru jest wizualizowany poniżej.

A B C
D E F

W języku C/C++ tablica wielowymiarowa jest przechowywana w porządku wierszowym. Innymi słowy, kolejne elementy wzdłuż wymiaru szerokości są przechowywane stale w przestrzeni pamięci liniowej.

Przesunięcie: 0 1 2 3 4 5
Wartość: A B C D E F

Krok w danym wymiarze to liczba elementów, które trzeba pominąć, aby uzyskać dostęp do następnego elementu w tym wymiarze. Kroki wyrażają układ tensoru w pamięci. W porządku wierszowo-majorowym, krok dla wymiaru szerokości jest zawsze równy 1, ponieważ sąsiadujące elementy wzdłuż wymiaru są przechowywane kontynuacyjnie. Przesunięcie wzdłuż wymiaru wysokości zależy od rozmiaru wymiaru szerokości; w powyższym przykładzie, odległość między kolejnymi elementami wzdłuż wymiaru wysokości (na przykład od A do D) jest równa liczbie elementów w wymiarze szerokości tensoru (czyli 3 w tym przykładzie).

Aby zilustrować inny układ, rozważ kolejność kolumny głównej. Innymi słowy, kolejne elementy wzdłuż wymiaru wysokości są przechowywane stale w przestrzeni pamięci liniowej. W takim przypadku krok wysokości jest zawsze 1, a krok szerokości wynosi 2 (rozmiar wymiaru wysokości).

Przesunięcie: 0 1 2 3 4 5
Wartość: A D B E C F

Wyższe wymiary

Jeśli chodzi o więcej niż dwa wymiary, odwoływanie się do układu jako o układzie wierszowym lub kolumnowym staje się niewygodne. W pozostałej części tego tematu są używane terminy i etykiety, takie jak te.

  • 2D: "HW" — wysokość jest wymiarem najwyższego porządku (wiersz główny).
  • 2D: "WH" — szerokość jest wymiarem o najwyższej kolejności (kolumna główna).
  • 3D: "DHW" — głębokość jest wymiarem najwyższego rzędu, a następnie wysokością, a następnie szerokością.
  • 3D: "WHD" — szerokość jest wymiarem najwyższego porządku, a następnie wysokością, a następnie głębokością.
  • 4D: "NCHW" — liczba obrazów (rozmiar partii), a następnie liczba kanałów, a następnie wysokość, a następnie szerokość.

Ogólnie rzecz biorąc, zapakowany krok wymiaru jest równy produktowi rozmiarów wymiarów niższej kolejności. Na przykład w układzie "DHW" krok D jest równy H * W; wartość H-stride jest równa W; a krok W jest równy 1. Mówi się, że kroki są pakowane , gdy całkowity rozmiar fizyczny tensor jest równy całkowitemu rozmiarowi logicznemu tensoru; innymi słowy, nie ma dodatkowej przestrzeni ani nakładających się elementów.

Rozszerzmy przykład 2D na trzy wymiary, tak abyśmy mieli tensor o głębokości 2, wysokości 2 i szerokości 3 (łącznie 12 elementów logicznych).

A B C
D E F

G H I
J K L

W układzie "DHW" ten tensor jest przechowywany w następujący sposób.

Przesunięcie: 0 1 2 3 4 5 6 7 8 9 10 11
Wartość: A B C D E F G H Ja J K L
  • D-stride = wysokość (2) * szerokość (3) = 6 (na przykład, jest to odległość między "A" a "G").
  • H-stride = szerokość (3) = 3 (na przykład odległość pomiędzy 'A' a 'D').
  • W-stride = 1 (na przykład odległość między "A" i "B").

Iloczyn skalarny indeksów/współrzędnych elementu i kroków wyznacza przesunięcie do tego elementu w buforze. Na przykład przesunięcie elementu H (d=1, h=0, w=1) wynosi 7.

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

Pakowane tensory

Powyższe przykłady ilustrują spakowane tensory. Uznaje się, że tensor jest pakowany wtedy, gdy rozmiar logiczny tensora (w elementach) jest równy fizycznemu rozmiarowi bufora (w elementach), a każdy element ma unikatowy adres/przesunięcie. Na przykład tensor 2x2x3 jest uznawany za spakowany, jeśli bufor ma długość 12 elementów, a żaden z par elementów nie dzieli tego samego przesunięcia w buforze. Pakowane tensory są najbardziej typowym przypadkiem; jednakże kroki umożliwiają bardziej złożone układy pamięci.

Nadawanie za pomocą kroków

Jeśli rozmiar buforu tensora (w elementach) jest mniejszy niż produkt jego wymiarów logicznych, wynika z tego, że musi istnieć pewne nakładające się elementy. Typowym przypadkiem tego zjawiska jest to, co nazywamy broadcastingiem; oznacza to, że elementy jednego wymiaru są kopiami innego wymiaru. Na przykład wróćmy do przykładu 2D. Załóżmy, że chcemy tensor, który jest logicznie 2x3, ale drugi wiersz jest identyczny z pierwszym wierszem. Oto jak to wygląda.

A B C
A B C

Można to przechowywać jako pakowany tensor HW/row-major. Jednak bardziej kompaktowy magazyn będzie zawierać tylko 3 elementy (A, B i C) i używać kroku wysokości 0 zamiast 3. W tym przypadku rozmiar fizyczny tensor wynosi 3 elementy, ale rozmiar logiczny to 6 elementów.

Ogólnie rzecz biorąc, jeśli krok wymiaru wynosi 0, wszystkie elementy w wymiarach niższej kolejności są powtarzane wzdłuż rozgłaszanego wymiaru; na przykład jeśli tensor to NCHW, a wartość kroku C wynosi 0, każdy kanał ma te same wartości wzdłuż H i W.

Dopełnianie z krokami

Mówi się, że tensor jest wypełniony, jeśli jego rozmiar fizyczny jest większy niż minimalny rozmiar potrzebny, aby pomieścić jego elementy. Jeśli nie ma emisji ani nakładających się elementów, minimalny rozmiar tensoru (w elementach) jest po prostu produktem jego wymiarów. Aby obliczyć DMLCalcBufferTensorSize rozmiar buforu dla tensorów DirectML, możesz użyć funkcji pomocnika DirectML (zobacz funkcje pomocnika DirectML, aby uzyskać listę tej funkcji).

Załóżmy, że bufor zawiera następujące wartości (elementy "x" wskazują wartości wypełnienia).

0 1 2 3 4 5 6 7 8 9
A B C x x D E F x x

Tensor wyściełany można opisać przy użyciu przesunięcia w pionie 5 zamiast 3. Zamiast przechodzenia przez 3 elementy, aby przejść do następnego wiersza, krok to 5 elementów (3 rzeczywiste elementy plus 2 elementy dopełniania). Wypełnienie jest powszechnie stosowane w grafice komputerowej, na przykład aby upewnić się, że obraz ma wyrównanie do potęgi dwóch.

A B C
D E F

Opisy tensora buforów DirectML

DirectML może pracować z różnymi fizycznymi układami tensorowymi, ponieważ struktura DML_BUFFER_TENSOR_DESC ma zarówno Sizes jak i Strides członków. Niektóre implementacje operatorów mogą być wydajniejsze z określonym układem, dlatego nie rzadko zmienia się sposób przechowywania danych tensorowych w celu uzyskania lepszej wydajności.

Większość operatorów DirectML wymaga tensorów 4D lub 5D, a kolejność rozmiarów i wartości kroków jest stała. Ustawiając kolejność rozmiarów i wartości kroków w opisie tensora, można wywnioskować różne układy fizyczne za pomocą języka DirectML.

Rozdzielczość 4D

Rozdzielczość 5D

  • DML_BUFFER_TENSOR_DESC::Rozmiary = { N-rozmiar, C-rozmiar, D-rozmiar, H-rozmiar, W-rozmiar }
  • DML_BUFFER_TENSOR_DESC::Kroki = { N-krok, C-krok, D-krok, H-krok, W-krok }

Jeśli operator DirectML wymaga tensora 4D lub 5D, ale rzeczywiste dane mają mniejszą rangę (na przykład 2D), wymiary wiodące powinny być wypełnione jedynkami. Na przykład tensor "HW" jest ustawiany przy użyciu DML_BUFFER_TENSOR_DESC::Sizes = { 1, 1, H, W }.

Jeśli dane tensora są przechowywane w formacie NCHW/NCDHW, to nie jest konieczne ustawianie DML_BUFFER_TENSOR_DESC::Strides, chyba że chcesz korzystać z broadcastingu lub wypełniania. Możesz ustawić pole kroków na nullptr. Jeśli jednak dane tensor są przechowywane w innym układzie, takim jak NHWC, potrzebne są kroki, aby wyrazić transformację z NCHW do tego układu.

W prostym przykładzie rozważmy opis tensora 2D o wysokości 3 i szerokości 5.

Pakowane NCHW (niejawne kroki)

  • DML_BUFFER_TENSOR_DESC::Rozmiary = { 1, 1, 3, 5 }
  • DML_BUFFER_TENSOR_DESC::Kroki = nullptr

Pakowane NCHW (wyraźne kroki)

  • N-krok = Rozmiar C * Rozmiar H * Rozmiar W = 1 * 3 * 5 = 15
  • Krok C = Rozmiar H * Rozmiar W = 3 * 5 = 15
  • H-przesunięcie = W-rozmiar = 5
  • Krok W = 1
  • DML_BUFFER_TENSOR_DESC::Rozmiary = { 1, 1, 3, 5 }
  • DML_BUFFER_TENSOR_DESC::Kroki = { 15, 15, 5, 1 }

Zapakowane NHWC

  • N-krok = Rozmiar H * Rozmiar W * Rozmiar C = 3 * 5 * 1 = 15
  • h-stride = w-size * c-size = 5 * 1 = 5
  • Krok W = Rozmiar C = 1
  • Krok C = 1
  • DML_BUFFER_TENSOR_DESC::Rozmiary = { 1, 1, 3, 5 }
  • DML_BUFFER_TENSOR_DESC::Kroki = { 15, 1, 5, 1 }

Zobacz także