Udostępnij przez


DirectMLX

DirectMLX to biblioteka pomocnicza tylko z nagłówkami w języku C++ dla DirectML, ułatwiająca komponowanie pojedynczych operatorów w grafy.

DirectMLX zapewnia wygodne otoki dla wszystkich typów operatorów DirectML (DML), a także intuicyjne przeciążenia operatorów, co ułatwia tworzenie wystąpień operatorów DML i łączenie ich w złożone wykresy.

Gdzie można znaleźć DirectMLX.h

DirectMLX.h jest dystrybuowany jako oprogramowanie typu open source w ramach licencji MIT. Najnowszą wersję można znaleźć w witrynie GitHub DirectML.

Wymagania dotyczące wersji

Język DirectMLX wymaga języka DirectML w wersji 1.4.0 lub nowszej (zobacz Historia wersji języka DirectML). Starsze wersje języka DirectML nie są obsługiwane.

Język DirectMLX.h wymaga kompilatora obsługującego język C++11, w tym (ale nie tylko):

  • Visual Studio 2017
  • Visual Studio 2019
  • Clang 10

Należy pamiętać, że kompilator języka C++17 (lub nowszego) jest opcją zalecaną przez nas. Kompilowanie dla języka C++11 jest możliwe, ale wymaga użycia bibliotek innych firm (takich jak GSL i Abseil), aby zastąpić brakujące standardowe funkcje biblioteki.

Jeśli masz konfigurację, która nie udaje się skompilować DirectMLX.h, prosimy zgłosić problem na naszym GitHubie.

Podstawowy sposób użycia

#include <DirectML.h>
#include <DirectMLX.h>

IDMLDevice* device;

/* ... */

dml::Graph graph(device);

// Input tensor of type FLOAT32 and sizes { 1, 2, 3, 4 }
auto x = dml::InputTensor(graph, 0, dml::TensorDesc(DML_TENSOR_DATA_TYPE_FLOAT32, {1, 2, 3, 4}));

// Create an operator to compute the square root of x
auto y = dml::Sqrt(x);

// Compile a DirectML operator from the graph. When executed, this compiled operator will compute
// the square root of its input.
DML_EXECUTION_FLAGS flags = DML_EXECUTION_FLAG_NONE;
ComPtr<IDMLCompiledOperator> op = graph.Compile(flags, { y });

// Now initialize and dispatch the DML operator as usual

Oto kolejny przykład, który tworzy graf DirectML zdolny do obliczania formuły kwadratowej.

#include <DirectML.h>
#include <DirectMLX.h>

IDMLDevice* device;

/* ... */

std::pair<dml::Expression, dml::Expression>
    QuadraticFormula(dml::Expression a, dml::Expression b, dml::Expression c)
{
    // Quadratic formula: given an equation of the form ax^2 + bx + c = 0, x can be found by:
    //   x = -b +/- sqrt(b^2 - 4ac) / (2a)
    // https://en.wikipedia.org/wiki/Quadratic_formula

    // Note: DirectMLX provides operator overloads for common mathematical expressions. So for 
    // example a*c is equivalent to dml::Multiply(a, c).
    auto x1 = -b + dml::Sqrt(b*b - 4*a*c) / (2*a);
    auto x2 = -b - dml::Sqrt(b*b - 4*a*c) / (2*a);

    return { x1, x2 };
}

/* ... */

dml::Graph graph(device);

dml::TensorDimensions inputSizes = {1, 2, 3, 4};
auto a = dml::InputTensor(graph, 0, dml::TensorDesc(DML_TENSOR_DATA_TYPE_FLOAT32, inputSizes));
auto b = dml::InputTensor(graph, 1, dml::TensorDesc(DML_TENSOR_DATA_TYPE_FLOAT32, inputSizes));
auto c = dml::InputTensor(graph, 2, dml::TensorDesc(DML_TENSOR_DATA_TYPE_FLOAT32, inputSizes));

auto [x1, x2] = QuadraticFormula(a, b, c);

// When executed with input tensors a, b, and c, this compiled operator computes the two outputs
// of the quadratic formula, and returns them as two output tensors x1 and x2
DML_EXECUTION_FLAGS flags = DML_EXECUTION_FLAG_NONE;
ComPtr<IDMLCompiledOperator> op = graph.Compile(flags, { x1, x2 });

// Now initialize and dispatch the DML operator as usual

Więcej przykładów

Kompletne przykłady przy użyciu języka DirectMLX można znaleźć w repozytorium GitHub DirectML.

Opcje czasu kompilacji

DirectMLX obsługuje dyrektywy preprocesora #define używane w czasie kompilacji do dostosowywania różnych części nagłówka.

Opcja Opis
DMLX_NO_EXCEPTIONS Jeśli zdefiniowane, powoduje, że błędy skutkują wywołaniem std::abort zamiast zgłaszania wyjątku. Jest to definiowane domyślnie, jeśli wyjątki są niedostępne (na przykład jeśli wyjątki zostały wyłączone w opcjach kompilatora).
DMLX_USE_WIL Jeśli #define, wyjątki są zgłaszane przy użyciu typów wyjątków Windows Implementation Library. W przeciwnym razie używane są standardowe typy wyjątków (takie jak std::runtime_error) . Ta opcja nie ma wpływu, jeśli DMLX_NO_EXCEPTIONS jest zdefiniowana.
DMLX_USE_ABSEIL Jeśli #define jest zdefiniowane, używa Abseil jako bezpośrednich zamienników dla typów biblioteki standardowej niedostępnych w C++11. Te typy obejmują absl::optional (zamiast std::optional), absl::Span (zamiast std::span), i absl::InlinedVector.
DMLX_USE_GSL Określa, czy należy używać biblioteki GSL jako zamiany elementu std::span. Jeśli zdefiniowano #define, użycia std::span są zastępowane przez gsl::span na kompilatorach bez natywnych implementacji std::span. W przeciwnym razie zamiast tego jest udostępniana implementacja wbudowanej listy rozwijanej. Należy pamiętać, że ta opcja jest używana tylko podczas kompilowania na kompilatorze pre-C++20 bez obsługi std::span, i gdy nie jest używane żadne inne zastąpienie standardowej biblioteki (takie jak Abseil).

Sterowanie układem tensorów

W przypadku większości operatorów funkcja DirectMLX oblicza właściwości tensorów wyjściowych operatora w Twoim imieniu. Na przykład podczas wykonywania dml::Reduce w poprzek osi { 0, 2, 3 } z tensorem wejściowym rozmiarów { 3, 4, 5, 6 }, funkcja DirectMLX automatycznie oblicza właściwości tensora wyjściowego, w tym prawidłowy kształt { 1, 4, 1, 1 }.

Jednak inne właściwości tensora wyjściowego obejmują Strides, TotalTensorSizeInBytes i GuaranteedBaseOffsetAlignment. Domyślnie DirectMLX ustawia te właściwości tak, aby tensor nie miał przesunięcia, bez gwarantowanego wyrównania przesunięcia podstawowego i całkowitego rozmiaru tensora w bajtach obliczonego przez DMLCalcBufferTensorSize.

Język DirectMLX obsługuje możliwość dostosowywania tych właściwości tensor wyjściowych przy użyciu obiektów nazywanych zasadami tensor. TensorPolicy to dostosowywalne wywołanie zwrotne wywoływane przez DirectMLX, które zwraca właściwości tensora danych wyjściowych, biorąc pod uwagę obliczony typ danych, flagi i rozmiary.

Zasady tensor można ustawić na obiekcie dml::Graph i będą używane dla wszystkich kolejnych operatorów na tym grafie. Zasady tensor można również ustawiać bezpośrednio podczas konstruowania tensorDesc.

Układ tensorów generowanych przez DirectMLX może zatem być kontrolowany przez ustawienie polityki TensorPolicy, która określa odpowiednie kroki na jego tensorach.

Przykład 1

// Define a policy, which is a function that returns a TensorProperties given a data type,
// flags, and sizes.
dml::TensorProperties MyCustomPolicy(
    DML_TENSOR_DATA_TYPE dataType,
    DML_TENSOR_FLAGS flags,
    Span<const uint32_t> sizes)
{
    // Compute your custom strides, total tensor size in bytes, and guaranteed base
    // offset alignment
    dml::TensorProperties props;
    props.strides = /* ... */;
    props.totalTensorSizeInBytes = /* ... */;
    props.guaranteedBaseOffsetAlignment = /* ... */;
    return props;
};

// Set the policy on the dml::Graph
dml::Graph graph(/* ... */);
graph.SetTensorPolicy(dml::TensorPolicy(&MyCustomPolicy));

Przykład 2

DirectMLX udostępnia również kilka alternatywnych strategii tensora wbudowanych. Zasady InterleavedChannel , na przykład, są udostępniane jako wygoda i może służyć do produkcji tensorów z krokami, tak aby były zapisywane w kolejności NHWC.

// Set the InterleavedChannel policy on the dml::Graph
dml::Graph graph(/* ... */);
graph.SetTensorPolicy(dml::TensorPolicy::InterleavedChannel());

// When executed, the tensor `result` will be in NHWC layout (rather than the default NCHW)
auto result = dml::Convolution(/* ... */);

Zobacz także