Z czego nauczyć się używać OpenCL?

Z czego nauczyć się używać OpenCL?
L9
  • Rejestracja: dni
  • Ostatnio: dni
  • Postów: 39
0

atomic mi nie działa, wynik jest taki sam jaki był.
W załączniku projekt dodaję

Wibowit
  • Rejestracja: dni
  • Ostatnio: dni
  • Lokalizacja: XML Hills
0

Po każdej instrukcji gdzie zwracany jest kod błędu, wypisz go. Czyli jakieś printf("%d\n", error); czy coś w ten deseń. W którymś miejscu prawdopodobnie wyskoczył ci błąd, a ty radośnie olewasz wszystkie błędy.

L9
  • Rejestracja: dni
  • Ostatnio: dni
  • Postów: 39
0

Według tego:
https://onedrive.live.com/redir?resid=35c2a5b0aaf9523b!1516&authkey=!ANKiPo_X2hEQRsc&v=3&ithint=photo%2cjpg
nie ma żadnego błędu.
A zrobiłem tak jak pisałeś i po każdej instrukcji zwracającej error wypisałem go przy pomocy:

Kopiuj
std::cout << error << std::endl; 
L9
  • Rejestracja: dni
  • Ostatnio: dni
  • Postów: 39
0

Ewentualnie zamiast sprawdzać dlaczego u mnie to nie działa może powiesz jak z tym sumowaniem hierarchicznym to wygląda?

Wibowit
  • Rejestracja: dni
  • Ostatnio: dni
  • Lokalizacja: XML Hills
0

Oprócz sprawdzania wyniku końcowego sprawdź sobie też wynik pośredni, czyli wykonanie tego kernela "Cmp".

Sumowanie hierarchiczne już przedstawiałem. Ale może jeszcze raz. Załóżmy, że każdy work-item sumuje 3 liczby i zapisuje wynik w drugiej tablicy.

Tablica wejściowa (11 elementów): 3, 4, 5, 8, 9, 0, 1, 2, 3, 5, 6
Sumowanie: 3+4+5, 8+9+0,1+2+3,5+6
Po zsumowaniu: 12,17,6,11

Kolejne przejście:
Tablica wejściowa: 12,17,6,11
Sumowanie: 12+17+6,11
Po zsumowaniu: 35,11

Kolejne przejście:
Tablica wejściowa: 35,11
Sumowanie: 35+11
Po zsumowaniu: 46

Został jeden element więc sumowanie jest zakończone.

Jak widać synchronizacja nie jest potrzebna bo work-itemy nie zapisują do tych samych komórek pamięci. Jednocześnie praca jest podzielona tak, by mogło być odpalonych naraz dużo work-itemów, co sprawi, że kernel będzie się szybko wykonywał na GPU, które może wykonywać wiele work-itemów naraz.

L9
  • Rejestracja: dni
  • Ostatnio: dni
  • Postów: 39
0

Tylko jak to zaimplementować? To co napisałeś zrozumiałem, nie wiem jak to zakodować. Pamiętaj, że nie mogę zsumować wszystkiego jak leci tylko co czwarty element tablicy.
Czyli jak mam przykład podany przez Ciebie to:
3+9+3 to pierwszy wynik
4+0+5 to drugi wynik
5+1+6 to trzeci wynik
8+2 to czwarty wynik.
Za to wracając do mojego projektu tablicą którą w powyższy sposób trzeba zsumować jest tablica c którą przekazuję do kernela Add i jak to zaimplementować?

Wracając do kernela Cmp. Ma on postać następującą:

Kopiuj
__kernel void Cmp(__global int* a, __global int* b, __global int* c, int size)
{
	
	int n = get_global_id(0);

	if (n < size)
	{
		c[n] = a[n] == b[n] ? 1 : 0;
	}
}
 

I działa poprawnie ponieważ wynik jego działań jest to trzecia dolna kolumna z poprzednich zrzutów.

Wibowit
  • Rejestracja: dni
  • Ostatnio: dni
  • Lokalizacja: XML Hills
0

No to dla ułatwienia możesz zrobić osobny kernel, który rozplątuje tablicę do czterech tablic. Wtedy przy sumowaniu nie będziesz musiał się tym martwić.

L9
  • Rejestracja: dni
  • Ostatnio: dni
  • Postów: 39
0

a jak potem wykonać to sumowanie? Ma to wyglądać jakoś w ten sposób?
gdzie

Kopiuj
 __kernel void Cmp(__global int* a, __global int* b, int size)
{
 
    int n = get_global_id(0);
 
    if (n < size)
    {
         a[n] = b[n] + b[n+1] +b[n+2];
}
 

gdzie size jest wielkością tablicy a ?

Wibowit
  • Rejestracja: dni
  • Ostatnio: dni
  • Lokalizacja: XML Hills
0

Bardziej coś w ten deseń:

Kopiuj
 __kernel void PartialAdd(__global int* a, __global int* b, int aSize) {
    int bi = get_global_id(0);
    int ai = bi * 3;
    int br = 0;
    for (int i = 0; i < 3; i++) {
        if (ai + i < aSize) {
             br += a[ai + i];
        }
    }
    b[bi] = br;
}
L9
  • Rejestracja: dni
  • Ostatnio: dni
  • Postów: 39
0

możesz mi opisać zmienne? bo tak, o ile dobrze rozumuję to
a jest to tablica wynikowa,
b to tablica z danymi.
aSize wielkość tablicy a,
bi identyfikator,
br wynik cząstkowy,
ale dalczego:
ai = bi * 3;
a[ai + i];
dlaczego tak to jest zrobione?

i czy mógłbyś mi wytłumaczyć prostymi słowami na jakiej zasadzie działa get_global_id(0);? Dlaczego argumentem jest 0?

Wibowit
  • Rejestracja: dni
  • Ostatnio: dni
  • Lokalizacja: XML Hills
1

bi to indeks w tablicy wynikowej b
ai to początkowy indeks w tablicy źródłowej a
ai = bi * 3, ponieważ sumujemy trójki, więc w tablicy a zaczynamy od 3x dalszego miejsca niż w b
a[ai + i] ponieważ sumujemy trójki, a więc efektywnie będzie to a[ai + 0], a[ai + 1] i a[ai + 2]

opis get_global_id masz tutaj: https://www.khronos.org/registry/cl/sdk/1.0/docs/man/xhtml/get_global_id.html
0 jest dlatego, że odpowiada on pierwszemu wymiarowi. OpenCL pozwala na maksymalnie 3 wymiary, ale często używa się tylko jednego.

L9
  • Rejestracja: dni
  • Ostatnio: dni
  • Postów: 39
0

Zatem w momencie kiedy wykorzystywałbym 2wymiarową tablicę to jak by to wyglądało?
Załóżmy, że 'a' , 'b' i c są to tablice 2D. W tym wypadku jak by wyglądał ten kernel?

Kopiuj
__kernel void Cmp(__global int* a, __global int* b, __global int* c, int size)
{
	
	int n = get_global_id(0);

	if (n < size)
	{
		c[n] = a[n] == b[n] ? 1 : 0;
	}
}

Co należałoby zmienić? Co dodać?

Wibowit
  • Rejestracja: dni
  • Ostatnio: dni
  • Lokalizacja: XML Hills
0

Nie bawiłem się jeszcze w wywołania dwuwymiarowe w OpenCLu, ale wtedy musiałbyś użyć też get_global_id(1) żeby dowiedzieć się jaki masz drugi wymiar.

Wymiary tablic są niezależne od wymiarów wywołania kernela. Żadna tablica nie jest 'główna'. Z tego powodu można się obejść bez wielowymiarowych wywołań i samemu sobie ręcznie kombinować z indeksami tablic.

L9
  • Rejestracja: dni
  • Ostatnio: dni
  • Postów: 39
0

A są gdzieś jakieś przykłady tablic 2-3 wymiarowych w oppenCL same kernele mnie obchodzą tak właściwie bo jeżeli tak to miałbym już całkowitą bazę pod mój projekt

Wibowit
  • Rejestracja: dni
  • Ostatnio: dni
  • Lokalizacja: XML Hills
0

Tutaj jest coś: http://www.thebigblob.com/gaussian-blur-using-opencl-and-the-built-in-images-textures/
Wygląda to na tablicę jednowymiarową + wielowymiarowe wywołanie kernela + ręczną zabawę z indeksami. Zabawa jest identyczna jak by to miało miejsce w C/ C++/ czymkolwiek. A więc jest sobie linijka:

Kopiuj
blurredImage[pos.x+pos.y*get_global_size(0)] = sum;

Czyli inaczej:

Kopiuj
index = x + y * Xsize;
blurredImage[index] = sum;

W ogólności kod w C/ C++ typu:

Kopiuj
int * tab = new int[Xsize * Ysize];
for (int y = 0; y < Ysize; y++) {
  for (int x = 0; x < Xsize; x++) {
    tab[x + y * Xsize] = cośtam;
  }
}

Można zastąpić dwuwymiarowym wywołaniem kernela o postaci:

Kopiuj
int x = get_global_id(0);
int y = get_global_id(1);
int Xsize = get_global_size(0);
tab[x + y * Xsize] = cośtam;
L9
  • Rejestracja: dni
  • Ostatnio: dni
  • Postów: 39
0

zatem tablice w programie deklarować w postaci:

Kopiuj
cl_int* a[x][y]; 

czy też:

Kopiuj
cl_int* a[x*y]; 

??

Zarejestruj się i dołącz do największej społeczności programistów w Polsce.

Otrzymaj wsparcie, dziel się wiedzą i rozwijaj swoje umiejętności z najlepszymi.