2010-09-30 6 views
13

Widziałem obie wersje w tutorialach, ale nie mogłem się dowiedzieć, jakie są ich wady i zalety. Który z nich jest właściwy?Jaka jest różnica między tworzeniem obiektu buforowego a clCreateBuffer + CL_MEM_COPY_HOST_PTR vs. clCreateBuffer + clEnqueueWriteBuffer?

cl_mem input = clCreateBuffer(context,CL_MEM_READ_ONLY,sizeof(float) * DATA_SIZE, NULL, NULL); 
clEnqueueWriteBuffer(command_queue, input, CL_TRUE, 0, sizeof(float) * DATA_SIZE, inputdata, 0, NULL, NULL); 

vs.

cl_mem input = clCreateBuffer(context,CL_MEM_READ_ONLY | CL_MEM_COPY_HOST_PTR, ,sizeof(float) * DATA_SIZE, inputdata, NULL); 

Dzięki.

[Aktualizacja]

I dodaje CL_MEM_COPY_HOST_PTR, drugiego przykładu, aby poprawne.

Odpowiedz

5

Zakładam, że inputdata nie ma wartości NULL.

W takim przypadku drugie podejście nie powinno działać w ogóle, ponieważ specyfikacja mówi, że clCreateBuffer zwraca NULL i błąd, jeżeli:

CL_INVALID_HOST_PTR jeśli host_ptr jest NULL i CL_MEM_USE_HOST_PTR lub CL_MEM_COPY_HOST_PTR są ustawione w flagami lub jeśli host_ptr nie ma wartości NULL, ale CL_MEM_COPY_HOST_PTR lub CL_MEM_USE_HOST_PTR nie są ustawione w flagach.

więc oznaczać albo

clCreateBuffer(context,CL_MEM_READ_ONLY | CL_MEM_COPY_HOST_PTR,sizeof(float) * DATA_SIZE, inputdata, NULL); 

lub

clCreateBuffer(context,CL_MEM_READ_ONLY | CL_MEM_USE_HOST_PTR,sizeof(float) * DATA_SIZE, inputdata, NULL); 

Pierwsza z nich powinna być mniej więcej taka sama, jak w pierwszym podejściu pokazałeś, podczas gdy druga nie będzie właściwie skopiuj dane, ale zamiast tego użyj dostarczonego miejsca w pamięci do przechowywania bufora (buforowanie lub jego części w pamięci urządzenia). To, która z nich jest lepsza, zależy oczywiście od scenariusza użycia.

Osobisty Wolę stosować podejście dwuetapowe polegające na pierwszym przydzieleniu bufora, a następnie wypełnieniu go funkcją writeToBuffer, ponieważ łatwiej jest zobaczyć, co się dzieje (oczywiście jeden krok może być szybszy (lub może nie, to po prostu przypuszczenie))

+1

Cześć Grizzly, miałeś rację. Zapomniałem CL_MEM_COPY_HOST_PTR. Więc nie ma twardych faktów przemawiających za jednym lub drugim? – Framester

+0

Przynajmniej ze specyfikacji nie powinno być. Oczywiście wydajność może się różnić (ale nie musi), ale to zależałoby od implementacji i może ulec zmianie, więc i tak nie liczę na to (jeśli wydajność ma krytyczne znaczenie dla transferu pamięci, może być interesujące przeanalizowanie asynchronicznych transferów pamięci do (używając CL_FALSE jako parametru blokującego clEnqueueWriteToBuffer) Znowu czy jego szybsza czy nie zależy od implementacji, dla CPU najszybszym powinno być użycie CL_USE_HOST_PTR. Ogólnie rzecz biorąc chciałbym się upewnić, że memtransfertime nie ma większego znaczenia i być donieckim, że – Grizzly

+0

Dzięki, wydajność nie jest taka ważna, to było bardziej akademickie pytanie. – Framester

1

Główną różnicą między tymi dwoma jest to, że pierwsza alokuje pamięć na urządzeniu, a następnie kopiuje dane do tej pamięci. Drugi tylko przydziela.

Czy miałeś na myśli clCreateBuffer(context,CL_MEM_READ_ONLY | CL_MEM_COPY_HOST_PTR,sizeof(float) * DATA_SIZE, inputdata, NULL);?

+0

Witam, Stevenhb, miałeś rację. Zapomniałem CL_MEM_COPY_HOST_PTR. Więc jak się teraz różnią? – Framester

+0

Nie sądzę, że tak, z wyjątkiem możliwości wykonywania asynchronicznych transferów, o czym wspomniał Grizzly. –

2

główną różnicą, że mam biegać w:

cl_mem input = clCreateBuffer(context,CL_MEM_READ_ONLY,sizeof(float) * DATA_SIZE, NULL, NULL); clEnqueueWriteBuffer(command_queue, input, CL_TRUE, 0, sizeof(float) * DATA_SIZE, inputdata, 0, NULL, NULL);

Ten pierwszy zestaw poleceń będzie utworzyć pusty bufor i enqueue polecenia w kolejce poleceń do wypełnienia bufora.

cl_mem input = clCreateBuffer(context,CL_MEM_READ_ONLY | CL_MEM_COPY_HOST_PTR, ,sizeof(float) * DATA_SIZE, inputdata, NULL) 

To drugie polecenie spowoduje utworzenie bufora i natychmiast go wypełni. Zauważ, że nie ma kolejki poleceń w tej liście argumentów, więc używa ona zawartości danych wejściowych, tak jak jest teraz.

Jeśli już korzystasz z kodu CL, a wskaźnik źródłowy jest zależny od poprzedniego polecenia w zakończeniu kolejki poleceń (np.zakodowany odczyt wcześniejszego bufora wyjściowego), zdecydowanie chcesz użyć pierwszej metody. Jeśli spróbujesz utworzyć i wypełnić bufor w jednym poleceniu, skończy się to warunkiem wyścigu, w którym zawartość bufora nie będzie poprawnie czekać na zakończenie odczytu poprzedniego bufora.

2

Fajnym aspektem pierwszego podejścia jest to, że "clEnqueueWriteBuffer" pozwala przypisać zdarzenie do kopii bufora. Powiedzmy, że chcesz zmierzyć czas potrzebny do skopiowania danych do procesora GPU przy użyciu opcji GPU_Profilowania, będziesz mógł to zrobić przy pierwszym podejściu, ale nie przy drugim.

Drugie podejście jest bardziej kompaktowe, łatwiejsze do odczytania i wymaga mniej linii do kodowania.

7

Podczas mojej pracy z OpenCL znalazłem bardzo istotną różnicę między

cl_mem CT = clCreateImage3DContext, CL_MEM_READ_ONLY | CL_MEM_COPY_HOST_PTR , Volume_format, X, Y, Z, rowPitch, slicePitch, sourceData, &error); 

i

cl_mem CT = clCreateImage3D(Context, CL_MEM_READ_ONLY , Volume_format, X, Y, Z, 0, 0, 0, &error); 
error = clEnqueueWriteImage(CommandQue, CT, CL_TRUE, origin, region, rowPitch, slicePitch, sourceData, 0, 0, 0); 

Na pierwszym podejściu OpenCL skopiuje wskaźnik host nie kierować do GPU. Najpierw przydzieli drugi bufor tymczasowy na hoście, co może spowodować problemy, jeśli załadujesz duże elementy, takie jak CT do GPU. Przez krótki czas wymagana pamięć jest dwukrotnie większa niż rozmiar CT. Również dane nie są kopiowane podczas tej funkcji. Jest on kopiowany podczas ustawiania argumentów do funkcji jądra, która używa obiektu obrazu 3D.

Drugie podejście bezpośrednio kopiuje dane do GPU. Nie ma dodatkowych przydziałów dokonanych przez OpenCL. Myślę, że jest to prawdopodobnie to samo w przypadku normalnych obiektów buforowych.