DirectMLX

DirectMLX ist eine C++ headerbasierte Hilfsprogramm-Bibliothek für DirectML, die das Verfassen einzelner Operatoren in Diagrammen erleichtert.

DirectMLX bietet bequeme Wrapper für alle DirectML(DML)-Operatortypen sowie intuitive Operatorüberladungen, wodurch es einfacher wird, DML-Operatoren zu instanziieren und in komplexe Diagramme zu verketten.

Speicherort DirectMLX.h

DirectMLX.h wird als Open-Source-Software unter der MIT-Lizenz verteilt. Die neueste Version finden Sie auf dem DirectML GitHub.

Versionsanforderungen

DirectMLX erfordert DirectML Version 1.4.0 oder höher (siehe DirectML-Versionsverlauf). Ältere DirectML-Versionen werden nicht unterstützt.

DirectMLX.h erfordert einen C++11-fähigen Compiler, einschließlich (aber nicht beschränkt auf):

  • Visual Studio 2017
  • Visual Studio 2019
  • Clang 10

Beachten Sie, dass ein C++17 (oder neuerer) Compiler die empfohlene Option ist. Das Kompilieren für C++11 ist möglich, erfordert jedoch die Verwendung von Drittanbieterbibliotheken (z . B. GSL und Abseil), um fehlende Standardbibliotheksfunktionen zu ersetzen.

Wenn Sie über eine Konfiguration verfügen, die nicht kompiliert werden kann, DirectMLX.hsenden Sie bitte ein Problem auf unserem GitHub.

Grundlegende Verwendung

#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

Hier ist ein weiteres Beispiel, das ein DirectML-Diagramm erstellt, das die quadratische Formel berechnen kann.

#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

Weitere Beispiele

Vollständige Beispiele mit DirectMLX finden Sie im DirectML GitHub-Repository.

Kompilierungszeitoptionen

DirectMLX unterstützt Kompilierzeit #define zum Anpassen verschiedener Teile des Headers.

Option Beschreibung
DMLX_NO_EXCEPTIONS Wenn #define, führt dies dazu, dass Fehler zu einem Aufruf an std::abort führen, anstatt eine Ausnahme auszuwerfen. Dies wird standardmäßig definiert, wenn Ausnahmen nicht verfügbar sind (z. B. wenn Ausnahmen in den Compileroptionen deaktiviert wurden).
DMLX_USE_WIL Wenn #define'd, werden Ausnahmen mithilfe von Ausnahmetypen der Windows-Implementierungsbibliothek ausgelöst. Andernfalls werden stattdessen Standard-Ausnahmetypen (z. B. std::runtime_error) verwendet. Diese Option hat keine Auswirkung, wenn DMLX_NO_EXCEPTIONS definiert ist.
DMLX_USE_ABSEIL Wenn #define'd, verwendet Abseil als Drop-In-Ersetzung für Standardbibliothekstypen, die in C++11 nicht verfügbar sind. Zu diesen Typen gehören absl::optional (anstelle von std::optional), absl::Span (anstelle von std::span) und absl::InlinedVector.
DMLX_USE_GSL Steuert, ob GSL als Ersatz verwendet std::spanwerden soll. Wenn #define'd, werden die Verwendungen std::span durch gsl::span an Compiler ohne systemeigene std::span Implementierungen ersetzt. Andernfalls wird stattdessen eine Inline-Drop-In-Implementierung bereitgestellt. Beachten Sie, dass diese Option nur bei der Kompilierung eines Vor-C++20-Compilers ohne Unterstützung für std::span verwendet wird und wenn keine andere Drop-In-Standardbibliotheksersetzung (z. B. Abseil) verwendet wird.

Steuern des Tensorlayouts

Für die meisten Operatoren berechnet DirectMLX die Eigenschaften der Ausgabe-Tensoren des Operators in Ihrem Auftrag. Wenn Sie z. B. eine dml::Reduce übergreifende Achse { 0, 2, 3 } mit einem Eingabe-Tensor von Größen { 3, 4, 5, 6 } ausführen, berechnet DirectMLX automatisch die Eigenschaften des Ausgabe-Tensors einschließlich der richtigen Form von { 1, 4, 1, 1 }.

Die anderen Eigenschaften eines Ausgabe-Tensors umfassen jedoch Strides, TotalTensorSizeInBytes und GuaranteedBaseOffsetAlignment. Standardmäßig legt DirectMLX diese Eigenschaften so fest, dass der Tensor keine Abweichungen, keine garantierte Basisoffsetausrichtung und eine Gesamt-Tensor-Größe in Byte wie von DMLCalcBufferTensorSize berechnet hat.

DirectMLX unterstützt die Möglichkeit, diese Ausgabe-Tensoreigenschaften mithilfe von Objekten anzupassen, die als Tensor Policies bezeichnet werden. Eine TensorPolicy ist ein anpassbarer Rückruf, der von DirectMLX aufgerufen wird, und gibt Ausgabe-Tensoreigenschaften zurück, die dem berechneten Datentyp, Flags und Größen eines Tensors gegeben sind.

Tensor Policies können für das dml::Graph-Objekt festgelegt werden und werden für alle nachfolgenden Operatoren in diesem Diagramm verwendet. Tensorrichtlinien können auch direkt beim Erstellen eines TensorDesc festgelegt werden.

Das Layout von Tensoren, die von DirectMLX erzeugt werden, kann daher durch Festlegen einer TensorPolicy gesteuert werden, die die entsprechenden Schritte auf seine Tensoren festlegt.

Beispiel 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));

Beispiel 2

DirectMLX bietet auch einige alternative Tensorrichtlinien, die integriert sind. Die InterleavedChannel-Richtlinie wird z. B. als Komfort bereitgestellt und kann verwendet werden, um Tensoren mit Fortschritten zu produzieren, sodass sie in NHWC-Reihenfolge geschrieben werden.

// 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(/* ... */);

Weitere Informationen