Udostępnij przez


Wiązanie w DirectML

W DirectML wiązanie odnosi się do przypisania zasobów do potoku dla GPU w trakcie inicjowania i wykonywania operatorów uczenia maszynowego. Te zasoby mogą być na przykład tensorami wejściowymi i wyjściowymi, a także dowolnymi tymczasowymi lub trwałymi zasobami, których potrzebuje operator.

W tym temacie przedstawiono koncepcyjne i proceduralne szczegóły wiązania. Zalecamy również pełne zapoznanie się z dokumentacją wywoływanych interfejsów API, w tym parametrów i uwag.

Ważne pomysły dotyczące wiązania

Poniższa lista kroków zawiera ogólny opis zadań związanych z powiązaniem. Należy wykonać te kroki za każdym razem, gdy wykonujesz dispatchable — dispatchable to albo inicjator operatora, albo skompilowany operator. Niniejsze kroki przedstawiają ważne koncepcje, struktury i metody związane z powiązaniem DirectML.

W kolejnych sekcjach w tym temacie bardziej szczegółowo opisano te zadania wiązania i wyjaśniono je z ilustracyjnymi fragmentami kodu pobranymi z minimalnego przykładu kodu aplikacji DirectML .

  • Wywołaj metodę IDMLDispatchable::GetBindingProperties na dyspozytorze, aby określić, ile deskryptorów potrzebuje oraz jakie są jego potrzeby dotyczące tymczasowych/trwałych zasobów.
  • Utwórz stertę deskryptorów Direct3D 12 wystarczająco dużą dla deskryptorów i powiąż ją z potokiem.
  • Wywołaj metodę IDMLDevice::CreateBindingTable , aby utworzyć tabelę powiązań DirectML reprezentującą zasoby powiązane z potokiem. Użyj struktury DML_BINDING_TABLE_DESC, aby opisać tabelę powiązań, w tym podzbiór deskryptorów, które są wskazywane przez nią w stercie deskryptorów.
  • Utwórz zasoby tymczasowe/trwałe jako zasoby buforu Direct3D 12, opisz je za pomocą struktur DML_BUFFER_BINDING i DML_BINDING_DESC i dodaj je do tabeli powiązań.
  • Jeśli moduł jest skompilowanym operatorem, utwórz bufor elementów tensora jako zasób buforowy Direct3D 12. Wypełnij/prześlij go, opisz za pomocą struktur DML_BUFFER_BINDING i DML_BINDING_DESC, a następnie dodaj go do tabeli wiązań.
  • Przekaż tabelę powiązań jako parametr podczas wywoływania IDMLCommandRecorder::RecordDispatch.

Pobieranie właściwości powiązania przekazywalnego

Struktura DML_BINDING_PROPERTIES opisuje potrzeby powiązania związane z operatorem rozdzielczym (inicjator operatora lub skompilowany operator). Te właściwości związane z powiązaniem obejmują liczbę deskryptorów, które należy powiązać z możliwością wysłania, a także rozmiar w bajtach dowolnego tymczasowego i/lub trwałego zasobu, którego potrzebuje.

Uwaga / Notatka

Nawet w przypadku różnych operatorów tego samego typu nie należy zakładać, że mają te same wymagania dotyczące powiązania. Wykonaj zapytanie o właściwości powiązania dla każdego tworzonego inicjatora i operatora.

Wywołaj metodę IDMLDispatchable::GetBindingProperties, aby pobrać DML_BINDING_PROPERTIES.

winrt::com_ptr<::IDMLCompiledOperator> dmlCompiledOperator;
// Code to create and compile a DirectML operator goes here.

DML_BINDING_PROPERTIES executeDmlBindingProperties{
    dmlCompiledOperator->GetBindingProperties()
};

winrt::com_ptr<::IDMLOperatorInitializer> dmlOperatorInitializer;
// Code to create a DirectML operator initializer goes here.

DML_BINDING_PROPERTIES initializeDmlBindingProperties{
    dmlOperatorInitializer->GetBindingProperties()
};

UINT descriptorCount = ...

Wartość descriptorCount, którą tutaj pobierasz, określa (minimalny) rozmiar sterty deskryptorów i tabel powiązań, które tworzysz podczas następnych dwóch kroków.

DML_BINDING_PROPERTIES zawiera również członka, który określa minimalny rozmiar zasobu tymczasowego w bajtach, który musi być powiązany z tabelą powiązań dla tego obiektu możliwego do wysłania. Wartość zero oznacza, że zasób tymczasowy nie jest wymagany.

Członek PersistentResourceSize, który jest minimalnym rozmiarem w bajtach persistentnego zasobu, który musi być powiązany z tabelą powiązań dla tego obiektu przeznaczonego do wysłania. Wartość zero oznacza, że zasób trwały nie jest wymagany. Zasób trwały, jeśli jest potrzebny, należy podać podczas inicjowania skompilowanego operatora (gdzie jest powiązany jako dane wyjściowe inicjatora operatora), a także podczas wykonywania. Więcej informacji na ten temat znajdziesz w dalszej części tego tematu. Tylko kompilowane operatory mają trwałe zasoby — inicjatory operatorów zawsze zwracają wartość 0 dla tego elementu członkowskiego.

Jeśli wywołasz metodę IDMLDispatchable::GetBindingProperties na inicjatorze operatora zarówno przed, jak i po wywołaniu metody IDMLOperatorInitializer::Reset, nie ma gwarancji, że dwa pobrane zestawy właściwości powiązania będą identyczne.

Opisz, stwórz i zwiąż stertę deskryptorów

Jeśli chodzi o deskryptory, twoja odpowiedzialność zaczyna się i kończy na stosie deskryptora. Sam język DirectML zajmuje się tworzeniem deskryptorów wewnątrz podanej sterty i zarządzaniem nimi.

Należy użyć struktury D3D12_DESCRIPTOR_HEAP_DESC, aby opisać stertę wystarczająco dużą dla liczby deskryptorów wymaganych przez obiekt zarządzany. Następnie utwórz go za pomocą metody ID3D12Device::CreateDescriptorHeap. Na koniec wywołaj metodę ID3D12GraphicsCommandList::SetDescriptorHeaps, aby powiązać stos deskryptorów z pipeline'em.

winrt::com_ptr<::ID3D12DescriptorHeap> d3D12DescriptorHeap;

D3D12_DESCRIPTOR_HEAP_DESC descriptorHeapDescription{};
descriptorHeapDescription.Type = D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV;
descriptorHeapDescription.NumDescriptors = descriptorCount;
descriptorHeapDescription.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE;

winrt::check_hresult(
    d3D12Device->CreateDescriptorHeap(
        &descriptorHeapDescription,
        _uuidof(d3D12DescriptorHeap),
        d3D12DescriptorHeap.put_void()
    )
);

std::array<ID3D12DescriptorHeap*, 1> d3D12DescriptorHeaps{ d3D12DescriptorHeap.get() };
d3D12GraphicsCommandList->SetDescriptorHeaps(
    static_cast<UINT>(d3D12DescriptorHeaps.size()),
    d3D12DescriptorHeaps.data()
);

Opisywanie i tworzenie tabeli powiązań

Tabela powiązań DirectML reprezentuje zasoby, które wiążesz z potokiem, aby były używane przez zadania do wykonania. Te zasoby mogą być tensorami wejściowymi i wyjściowymi (lub innymi parametrami) dla operatora albo różnymi trwałymi i tymczasowymi zasobami, z których korzysta jednostka wykonawcza.

Użyj struktury DML_BINDING_TABLE_DESC, aby opisać tabelę powiązań, w tym element wywoływalny, dla którego tabela powiązań będzie reprezentować powiązania, oraz zakres deskryptorów (z sterty deskryptorów, która została właśnie utworzona), do której tabela powiązań ma się odwoływać (i do której DirectML może zapisywać deskryptory). Wartość descriptorCount (jedna z właściwości powiązania, które pobraliśmy w pierwszym kroku) informuje nas, jaki minimalny rozmiar jest w deskryptorach tabeli powiązań wymaganej do wysłania obiektu. W tym miejscu używamy tej wartości, aby wskazać maksymalną liczbę deskryptorów, które DirectML ma prawo zapisywać w stercie, zaczynając od obu dostarczonych uchwytów deskryptorów procesora CPU i procesora GPU.

Następnie wywołaj metodę IDMLDevice::CreateBindingTable , aby utworzyć tabelę powiązań DirectML. W kolejnych krokach, po utworzeniu dalszych zasobów możliwych do dyspozycji, dodamy te zasoby do tabeli powiązań.

Zamiast przekazywać DML_BINDING_TABLE_DESC w tym wywołaniu, można przekazać nullptr, wskazując pustą tabelę powiązań.

DML_BINDING_TABLE_DESC dmlBindingTableDesc{};
dmlBindingTableDesc.Dispatchable = dmlOperatorInitializer.get();
dmlBindingTableDesc.CPUDescriptorHandle = d3D12DescriptorHeap->GetCPUDescriptorHandleForHeapStart();
dmlBindingTableDesc.GPUDescriptorHandle = d3D12DescriptorHeap->GetGPUDescriptorHandleForHeapStart();
dmlBindingTableDesc.SizeInDescriptors = descriptorCount;

winrt::com_ptr<::IDMLBindingTable> dmlBindingTable;
winrt::check_hresult(
    dmlDevice->CreateBindingTable(
        &dmlBindingTableDesc,
        __uuidof(dmlBindingTable),
        dmlBindingTable.put_void()
    )
);

Kolejność zapisywania deskryptorów DirectML w stercie jest nieokreślona, dlatego aplikacja musi zachować ostrożność, aby nie nadpisywać deskryptorów zawartych w tabeli powiązań. Dostarczone uchwyty deskryptora procesora CPU i procesora GPU mogą pochodzić z różnych stertów, jednak wówczas aplikacja ponosi odpowiedzialność za zapewnienie, że cały zakres deskryptora, do którego odwołuje się uchwyt deskryptora procesora CPU, jest kopiowany do zakresu, do którego odwołuje się deskryptor procesora GPU przed wykonaniem przy użyciu tej tabeli powiązań. Sterta deskryptora, z którego podano uchwyty, musi mieć typ D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV. Ponadto sterta, do której odwołuje się GPUDescriptorHandle, musi być stertą deskryptora widocznego dla shadera.

Możesz zresetować tabelę powiązań, aby usunąć wszelkie zasoby, które do niej dodałeś, jednocześnie zmieniając dowolną właściwość, którą ustawiłeś na początkowej DML_BINDING_TABLE_DESC (aby opakować nowy zakres deskryptorów lub wykorzystać ją ponownie dla innego zadania do rozesłania). Wystarczy wprowadzić zmiany w strukturze opisu i wywołać metodę IDMLBindingTable::Reset.

dmlBindingTableDesc.Dispatchable = pIDMLCompiledOperator.get();

winrt::check_hresult(
    pIDMLBindingTable->Reset(
        &dmlBindingTableDesc
    )
);

Opisywanie i wiązanie wszelkich tymczasowych/trwałych zasobów

Struktura DML_BINDING_PROPERTIES wypełniona podczas pobierania właściwości powiązania elementu do wywołania zawiera rozmiar w bajtach dowolnego tymczasowego i/lub trwałego zasobu, którego wymaga element do wywołania. Jeśli którykolwiek z tych rozmiarów jest inny niż zero, utwórz zasób buforu Direct3D 12 i dodaj go do tabeli powiązań.

W poniższym przykładzie kodu tworzymy tymczasowy zasób o rozmiarze temporaryResourceSize bajtów dla elementu dyspozycyjnego. Opisujemy, jak chcemy powiązać zasób, a następnie dodamy to powiązanie do tabeli powiązań.

Ponieważ wiążemy pojedynczy zasób buforu, opisujemy powiązanie za pomocą struktury DML_BUFFER_BINDING . W tej strukturze określamy zasób buforu Direct3D 12 (zasób musi mieć wymiar D3D12_RESOURCE_DIMENSION_BUFFER), a także przesunięcie i rozmiar w buforze. Istnieje również możliwość opisania powiązania dla tablicy buforów (a nie dla pojedynczego buforu), a struktura DML_BUFFER_ARRAY_BINDING istnieje w tym celu.

Aby odjąć rozróżnienie między powiązaniem buforu a powiązaniem tablicy buforu, użyjemy struktury DML_BINDING_DESC . Możesz ustawić element członkowski Type na DML_BINDING_TYPE_BUFFER lub DML_BINDING_TYPE_BUFFER_ARRAY. Następnie można ustawić element członkowski Desc tak, aby wskazywał DML_BUFFER_BINDING lub DML_BUFFER_ARRAY_BINDING w zależności od Type.

W tym przykładzie mamy do czynienia z zasobem tymczasowym, dlatego dodamy go do tabeli powiązań z wywołaniem metody IDMLBindingTable::BindTemporaryResource.

D3D12_HEAP_PROPERTIES defaultHeapProperties{ CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_DEFAULT) };
winrt::com_ptr<::ID3D12Resource> temporaryBuffer;

D3D12_RESOURCE_DESC temporaryBufferDesc{ CD3DX12_RESOURCE_DESC::Buffer(temporaryResourceSize) };
winrt::check_hresult(
    d3D12Device->CreateCommittedResource(
        &defaultHeapProperties,
        D3D12_HEAP_FLAG_NONE,
        &temporaryBufferDesc,
        D3D12_RESOURCE_STATE_COMMON,
        nullptr,
        __uuidof(temporaryBuffer),
        temporaryBuffer.put_void()
    )
);

DML_BUFFER_BINDING bufferBinding{ temporaryBuffer.get(), 0, temporaryResourceSize };
DML_BINDING_DESC bindingDesc{ DML_BINDING_TYPE_BUFFER, &bufferBinding };
dmlBindingTable->BindTemporaryResource(&bindingDesc);

Zasób tymczasowy (jeśli jest potrzebny) to pamięć tymczasowa, która jest używana wewnętrznie podczas wykonywania operatora, więc nie musisz się martwić o jej zawartość. Nie musisz także go zatrzymywać po zakończeniu wywołania funkcji IDMLCommandRecorder::RecordDispatch na GPU. Oznacza to, że aplikacja może zwolnić lub zastąpić tymczasowy zasób między wywołaniami skompilowanego operatora. Podany zakres buforu, który ma być powiązany jako zasób tymczasowy, musi mieć wyrównanie początkowego przesunięcia do DML_TEMPORARY_BUFFER_ALIGNMENT. Typ sterty leżącej u podstaw buforu musi być D3D12_HEAP_TYPE_DEFAULT.

Jeśli jednak zasób dyspozycyjny zgłasza rozmiar niezerowy dla bardziej trwałego zasobu trwałego, procedura jest nieco inna. Należy utworzyć bufor i opisać powiązanie zgodnie z tym samym wzorcem, jak pokazano powyżej. Dodaj ją jednak do tabeli powiązań inicjatora operatora za pomocą wywołania metody IDMLBindingTable::BindOutputs, ponieważ jest to zadanie inicjatora operatora, aby zainicjować zasób trwały. Następnie dodaj ją do tabeli powiązań skompilowanego operatora za pomocą wywołania metody IDMLBindingTable::BindPersistentResource. Zobacz minimalny przykład kodu aplikacji DirectML , aby zobaczyć ten przepływ pracy w akcji. Zawartość i okres istnienia zasobu trwałego muszą być utrwalane tak długo, jak działa kompilowany operator. Oznacza to, że jeśli operator wymaga trwałego zasobu, aplikacja musi ją podać podczas inicjowania, a następnie dostarczyć ją do wszystkich przyszłych wykonań operatora bez modyfikowania jego zawartości. Zasób trwały jest zwykle używany przez DirectML do przechowywania tabel odnośników lub innych długotrwałych danych, które są obliczane podczas inicjowania operatora i wykorzystywane ponownie w przyszłych wykonaniach tego operatora. Podany zakres bufora, który ma być powiązany jako bufor stały, musi mieć wyrównane przesunięcie początkowe do DML_PERSISTENT_BUFFER_ALIGNMENT. Typ sterty leżącej u podstaw buforu musi być D3D12_HEAP_TYPE_DEFAULT.

Opisywanie i wiązanie dowolnych tensorów

Jeśli masz do czynienia z operatorem skompilowanym (a nie z inicjatorem operatora), musisz powiązać zasoby wejściowe i wyjściowe (dla tensorów i innych parametrów) z tabelą powiązań operatora. Liczba powiązań musi dokładnie odpowiadać liczbie danych wejściowych operatora, w tym opcjonalnych tensorów. Określone tensory wejściowe i wyjściowe oraz inne parametry, które przyjmuje operator, są udokumentowane w temacie dla tego operatora (na przykład DML_ELEMENT_WISE_IDENTITY_OPERATOR_DESC).

Zasób tensor jest buforem zawierającym poszczególne wartości elementu tensor. Przesyłanie i odczytanie takiego bufora do/z procesora GPU przy użyciu standardowych technik Direct3D 12 (wgrywanie zasobów oraz odczytanie danych przez bufor). Zobacz minimalny przykład kodu aplikacji DirectML , aby zobaczyć te techniki w działaniu.

Na koniec opisz powiązania zasobów wejściowych i wyjściowych za pomocą struktur DML_BUFFER_BINDING i DML_BINDING_DESC , a następnie dodaj je do tabeli powiązań skompilowanego operatora z wywołaniami idMLBindingTable::BindInputs i IDMLBindingTable::BindOutputs. Podczas wywoływania metody IDMLBindingTable::Bind* kod DirectML zapisuje co najmniej jeden deskryptor do zakresu deskryptorów procesora CPU.

DML_BUFFER_BINDING inputBufferBinding{ inputBuffer.get(), 0, tensorBufferSize };
DML_BINDING_DESC inputBindingDesc{ DML_BINDING_TYPE_BUFFER, &inputBufferBinding };
dmlBindingTable->BindInputs(1, &inputBindingDesc);

DML_BUFFER_BINDING outputBufferBinding{ outputBuffer.get(), 0, tensorBufferSize };
DML_BINDING_DESC outputBindingDesc{ DML_BINDING_TYPE_BUFFER, &outputBufferBinding };
dmlBindingTable->BindOutputs(1, &outputBindingDesc);

Jednym z kroków tworzenia operatora DirectML (zobacz IDMLDevice::CreateOperator) jest zadeklarowanie co najmniej jednej struktury DML_BUFFER_TENSOR_DESC w celu opisania danych tensor pobieranych i zwracanych przez operatora. Oprócz typu i rozmiaru buforu tensor można opcjonalnie określić flagę DML_TENSOR_FLAG_OWNED_BY_DML .

DML_TENSOR_FLAG_OWNED_BY_DML wskazuje, że dane tensor powinny być własnością i zarządzane przez usługę DirectML. DirectML tworzy kopię danych tensor podczas inicjowania operatora i przechowuje je w zasobie trwałym. Dzięki temu język DirectML może przeprowadzać ponowne formatowanie danych tensorowych w innych, bardziej wydajnych formularzach. Ustawienie tej flagi może zwiększyć wydajność, ale zwykle jest przydatne tylko w przypadku tensorów, których dane nie zmieniają się przez okres istnienia operatora (na przykład tensor wagi). Flaga może być używana tylko w tensorach wejściowych. Gdy flaga jest ustawiona w określonym opisie tensoru, odpowiedni tensor musi być powiązany z tabelą powiązań podczas inicjowania operatora, a nie podczas wykonywania (co spowoduje błąd). Jest to przeciwieństwo zachowania domyślnego (zachowanie bez flagi DML_TENSOR_FLAG_OWNED_BY_DML), gdzie tensor ma być powiązany podczas wykonywania, a nie podczas inicjowania. Wszystkie zasoby powiązane z DirectML muszą być zasobami domyślnymi lub niestandardowymi stertami.

Aby uzyskać więcej informacji, zobacz IDMLBindingTable::BindInputs i IDMLBindingTable::BindOutputs.

Wykonywanie wysyłania

Przekaż tabelę powiązań jako parametr podczas wywoływania IDMLCommandRecorder::RecordDispatch.

Jeśli używasz tabeli powiązań podczas wywołania IDMLCommandRecorder::RecordDispatch, DirectML wiąże odpowiednie deskryptory GPU z potokiem. Uchwyty deskryptorów CPU i GPU nie muszą wskazywać tych samych wpisów w puli deskryptorów, jednak w takim przypadku aplikacja jest odpowiedzialna za to, aby cały zakres deskryptorów, do którego odwołuje się uchwyt deskryptora CPU, został skopiowany do zakresu, do którego odwołuje się uchwyt deskryptora GPU przed wykonaniem przy użyciu tej tabeli powiązań.

winrt::com_ptr<::ID3D12GraphicsCommandList> d3D12GraphicsCommandList;
// Code to create a Direct3D 12 command list goes here.

winrt::com_ptr<::IDMLCommandRecorder> dmlCommandRecorder;
// Code to create a DirectML command recorder goes here.

dmlCommandRecorder->RecordDispatch(
    d3D12GraphicsCommandList.get(),
    dmlOperatorInitializer.get(),
    dmlBindingTable.get()
);

Na koniec zamknij listę poleceń Direct3D 12 i prześlij ją do wykonania, tak jak na innej liście poleceń.

Przed wykonaniem RecordDispatch na GPU, należy przekazać wszystkie powiązane zasoby do stanu D3D12_RESOURCE_STATE_UNORDERED_ACCESS lub do stanu, który może być niejawnie promowany do D3D12_RESOURCE_STATE_UNORDERED_ACCESS, na przykład D3D12_RESOURCE_STATE_COMMON. Po zakończeniu tego wywołania zasoby pozostają w stanie D3D12_RESOURCE_STATE_UNORDERED_ACCESS . Jedynym wyjątkiem jest sterta przesyłana, gdy jest powiązana w trakcie inicjacji operatora i gdy co najmniej jeden tensor ma ustawioną flagę DML_TENSOR_FLAG_OWNED_BY_DML. W takim przypadku wszystkie sterty przesyłania przeznaczone do danych wejściowych muszą być w stanie D3D12_RESOURCE_STATE_GENERIC_READ i pozostaną w tym stanie, jak wymagane przez wszystkie sterty przesyłania. Jeśli DML_EXECUTION_FLAG_DESCRIPTORS_VOLATILE nie została ustawiona podczas kompilowania operatora, wszystkie powiązania muszą być ustawione w tabeli powiązań przed wywołaniem elementu RecordDispatch , w przeciwnym razie zachowanie jest niezdefiniowane. W przeciwnym razie, jeśli operator obsługuje późne powiązanie, powiązanie zasobów może zostać odroczone do momentu przesłania listy poleceń Direct3D 12 do kolejki poleceń do wykonania.

RecordDispatch działa logicznie jak wywołanie ID3D12GraphicsCommandList::Dispatch. W związku z tym bariery widoku dostępu nieuporządkowanego (UAV) są niezbędne, aby zapewnić poprawną kolejność, jeśli istnieją zależności danych między wysyłkami. Ta metoda nie wstawia barier UAV dla zasobów wejściowych ani wyjściowych. Aplikacja musi zapewnić, że prawidłowe bariery UAV są stosowane dla wszystkich danych wejściowych, kiedy ich zawartość zależy od przetwarzania nadrzędnego, oraz dla danych wyjściowych, kiedy istnieją podrzędne przetwarzania zależne od tych danych wyjściowych.

Okres istnienia i synchronizacja deskryptorów i tabeli powiązań

Dobrym modelem mentalnym powiązania w DirectML jest to, że za sceną sama tabela powiązań DirectML tworzy i zarządza deskryptorami nieuporządkowanego widoku dostępu (UAV, unordered access view) wewnątrz przekazanej przez ciebie stercie deskryptorów. Tak więc wszystkie zwykłe zasady Direct3D 12 mają zastosowanie w kontekście synchronizowania dostępu do tej sterty i jej deskryptorów. Aplikacja jest odpowiedzialna za prawidłową synchronizację pracy między CPU i GPU, korzystając z tabeli powiązań.

Tabela powiązań nie może zastąpić deskryptora, gdy deskryptor jest używany (na przykład przez poprzednią ramkę). Dlatego, jeśli chcesz ponownie użyć już powiązanej sterty deskryptorów (na przykład poprzez ponowne wywołanie Bind* na tabeli powiązań, która do niej się odnosi, lub ręczne nadpisanie sterty deskryptorów), powinieneś poczekać, aż element wykonujący, korzystający z tej sterty deskryptorów, zakończy swoje działanie na procesorze GPU. Tabela powiązań nie utrzymuje silnego odniesienia do sterty deskryptora, do której zapisuje, więc nie wolno zwalniać sterty deskryptora widocznej dla shadera, dopóki wszystkie działania używające tej tabeli powiązań nie zostaną zakończone na GPU.

Z drugiej strony, chociaż tabela powiązań określa i zarządza stertą deskryptorów, sama tabela nie zawiera tej pamięci. Dlatego możesz zwolnić lub zresetować tabelę powiązań w dowolnym momencie po wywołaniu IDMLCommandRecorder::RecordDispatch (nie trzeba czekać na ukończenie tego wywołania na GPU, wystarczy, że bazowe deskryptory pozostaną prawidłowe).

Tabela powiązań nie przechowuje silnych odwołań do żadnych zasobów powiązanych z nią — aplikacja musi upewnić się, że zasoby nie są usuwane, gdy są nadal używane przez procesor GPU. Ponadto tabela powiązań nie jest bezpieczna dla wątków — aplikacja nie może wywoływać metod dotyczących tabeli powiązań jednocześnie z różnych wątków bez synchronizacji.

Należy również wziąć pod uwagę, że w każdym przypadku ponowne powiązanie jest konieczne tylko wtedy, gdy zmienisz, które zasoby są powiązane. Jeśli nie musisz zmieniać powiązanych zasobów, możesz powiązać je raz podczas uruchamiania i przekazać tę samą tabelę powiązań za każdym razem, gdy wywołasz metodę RecordDispatch.

W przypadku przeplatania obciążeń uczenia maszynowego i renderowania upewnij się, że tabele powiązań każdej klatki wskazują zakresy sterty deskryptora, które nie są jeszcze używane na GPU.

Opcjonalnie określ powiązania operatorów z opóźnieniem

Jeśli masz do czynienia z operatorem skompilowanym (a nie z inicjatorem operatora), możesz określić opóźnione powiązanie dla operatora. Bez opóźnionego powiązania należy ustawić wszystkie powiązania w tabeli powiązań przed zarejestrowaniem operatora na liście poleceń. Za pomocą opóźnionego powiązania można ustawić (lub zmienić) powiązania dla operatorów, które już zostały zarejestrowane na liście poleceń, zanim lista poleceń zostanie przesłana do kolejki poleceń.

Aby określić późne powiązanie, wywołaj metodę IDMLDevice::CompileOperator z argumentem flagsDML_EXECUTION_FLAG_DESCRIPTORS_VOLATILE.

Zobacz także