logo elektroda
logo elektroda
X
logo elektroda
REKLAMA
REKLAMA
Adblock/uBlockOrigin/AdGuard mogą powodować znikanie niektórych postów z powodu nowej reguły.

Jak uruchomić program napisany w C na procesorze karty graficznej GPU?

MARTE.BEST 01 Lip 2026 01:10 309 5
REKLAMA
  • #1 21930729
    MARTE.BEST
    Poziom 7  
    Posty: 141
    Jak uruchomić program napisany w C na procesorze karty graficznej?
    Czy uzyskam wtedy przyśpieszenie?

    CPU to AMD Ryzen 5 3600 6 Core a GPU to NVidia GTX 1660.
    System Ubuntu 64 bit.

    Napisałem kilka programików w C, m.in apkę CPIARCH - Compute Pi Archimedes.
    Wykorzystuje Ona biblioteke MPFR. Kod źródłowy w innym moim wątku dotyczącym liczby Pi. Link: https://www.elektroda.pl/rtvforum/topic4164315.html

    Kompiluję ją za pomocą NVCC poleceniem: nvcc -gencode arch=compute_75,code=sm_75 -o cpiarch cpiarch.c -lmpfr

    Plik wykonywalny jest większy kilkukrotnie niż zwykły kompilowany przez GCC lecz przyśpieszenia nie widzę, czas wykonania zadania dla 100000 iteracji praktycznie taki sam (około 203 sekund). Poza tym po uruchomieniu TOP w CLI w zadaniach w tle widać, że obciążony jest tak samo jak zwykle procesor, a nie karta graficzna.
    Dodam, że aplikacja jest jednowątkowa i praktycznie to jest jedna pętla o dużym zakresie.

    Czy trzeba dodać jakieś dyrektywy w kodzie źródłowym oprócz tych poleceń dla kompilatora?
    Będę wdzięczny za każdą podpowiedź. CUDA to dla mnie nowy temat.
  • REKLAMA
  • Pomocny post
    #2 21930733
    K_o_s

    Poziom 26  
    Posty: 629
    Pomógł: 94
    Ocena: 116
    Zadałem Twój problem Perplexity, to mi odpowiedział:
    Krótko: samo skompilowanie programu w C przez `nvcc` absolutnie nie powoduje, że zacznie się wykonywać na GPU ani że zobaczysz przyspieszenie. Musisz jawnie napisać **kernel** CUDA, zaalokować pamięć na device, skopiować dane i uruchomić tysiące wątków w stylu SIMT; inaczej wszystko dalej leci na Ryzenie. [cpp0x](https://cpp0x.pl/forum/temat/?id=6677)
    
    ## Dlaczego teraz nie widzisz żadnego zysku
    
    - `cpiarch.c` jest zwykłym kodem C, używającym MPFR na CPU. [jankazimirski](https://jankazimirski.pl/files/PWR.04.pdf)
    - `nvcc` w takim trybie działa praktycznie jak wrapper na `gcc`: kompiluje kod hosta, ale nie tworzy żadnych jąder GPU, bo w kodzie ich nie ma. [pl.wikipedia](https://pl.wikipedia.org/wiki/CUDA)
    - MPFR nie jest biblioteką „GPU‑aware” – wszystkie operacje na liczbach wielkiej precyzji są wykonywane na CPU, w dodatku są mocno gałęziste i trudne do zrównoleglenia w stylu GPU. [cs.put.poznan](http://www.cs.put.poznan.pl/rwalkowiak/pliki/2019/pr/gpu_cuda_2019_pr.pdf)
    - TOP pokazuje obciążenie CPU, bo jedyny aktywny „compute device” to Ryzen; GPU siedzi bezczynnie, chyba że Xorg/Wayland coś renderuje. [pl.wikipedia](https://pl.wikipedia.org/wiki/Procesor_graficzny)
    
    Pojedynczy wątek na GPU (jedna CUDA „thread”) nie jest szybszy od pojedynczego wątku na CPU; GPU wygrywa wyłącznie wtedy, gdy masz setki–tysiące wątków wykonujących ten sam prosty kod na różnych danych (SIMT, coalesced memory, mało branchy). [cpp0x](https://cpp0x.pl/forum/temat/?id=6677)
    
    ## Co by trzeba zmienić, żeby faktycznie liczyć na GPU
    
    Minimalny „CUDA‑fication” algorytmu wygląda tak (schemat, nie pełny kod):
    
    1. **Rozdzielenie kodu na host i device.**  
       - Host (Ryzen): steruje obliczeniami, alokuje i kopiuje dane, uruchamia kernel, zbiera wynik.  
       - Device (GTX 1660): funkcja oznaczona jako `__global__` / `__device__`, kompilowana przez NVCC na kod SM (np. `sm_75`). [pl.wikipedia](https://pl.wikipedia.org/wiki/CUDA)
    
    2. **Design kernela:**  
       - Masz pętlę 100000 iteracji – to aż się prosi, żeby każda iteracja (lub blok iteracji) była niezależną pracą pojedynczego wątku (lub bloku).  
       - W CUDA:  
         ```c
         __global__ void pi_kernel(/* dane wejściowe, bufor wyników */) {
             int i = blockIdx.x * blockDim.x + threadIdx.x;
             if (i >= N) return;
             // oblicz częściowy wkład do pi dla iteracji i
             // wynik zapisz do wyniki[i]
         }
         ```  
         Host uruchamia coś w stylu `pi_kernel<<<gridSize, blockSize>>>(...)` i potem robi redukcję wyników na CPU albo GPU. [cpp0x](https://cpp0x.pl/forum/temat/?id=6677)
    
    3. **Usunięcie MPFR z „gorącej ścieżki” kernela:**  
       - Na GPU masz natywne `float`/`double`, ewentualnie „software’ową” kwadruple, ale nie MPFR. [jankazimirski](https://jankazimirski.pl/files/PWR.04.pdf)
       - Jeśli Twoja metoda Archimedesa wymaga bardzo wysokiej precyzji (setki–tysiące bitów mantysy), to typowy kernel CUDA zwyczajnie nie jest w stanie tego policzyć 1:1.  
       - Zysk z GPU uzyskasz dopiero, jeśli akceptujesz dolną precyzję (np. double) i równoleglisz dużo niezależnych zadań (np. liczenie pi różnymi metodami / parametrami albo dla ogromnej liczby punktów w metodach Monte Carlo). [cs.put.poznan](http://www.cs.put.poznan.pl/rwalkowiak/pliki/2016/ows/projekt_pkg2016_ows.pdf)
    
    4. **Pamięć i transfery:**  
       - Alokujesz na GPU: `cudaMalloc(&d_results, N * sizeof(double))`.  
       - Kopiuje się dane wejściowe: `cudaMemcpy(d_input, h_input, size, cudaMemcpyHostToDevice)`.  
       - Po wykonaniu kernela kopiujesz wyniki: `cudaMemcpy(h_results, d_results, size, cudaMemcpyDeviceToHost)`.  
       - Transfery przez PCIe mogą zjeść zysk, jeśli N jest małe lub obliczenia na element są lekkie. [pl.wikipedia](https://pl.wikipedia.org/wiki/CUDA)
    
    5. **Konfiguracja GTX 1660 pod CUDA:**  
       - Zainstalowany sterownik NVIDIA (proprietary) + CUDA Toolkit zgodny z wersją sterownika na Ubuntu 64‑bit. [blog.psychob](https://blog.psychob.pl/2019/11/24/konfiguracja-karty-graficznej-na-ubuntu/)
       - Sprawdzasz `nvidia-smi`, żeby widzieć aktywność GPU podczas uruchomienia programu – tam zobaczysz proces `cpiarch` tylko jeśli naprawdę uruchamiasz kernel. [pl.wikipedia](https://pl.wikipedia.org/wiki/Procesor_graficzny)
    
    ## Dlaczego Twój konkretny przypadek jest słabym kandydatem na GPU
    
    - Obliczenia w MPFR są „ciężkie” na poziomie pojedynczej operacji, z dużą ilością branchy, struktur danych i alokacji; GPU lubi masę prostych operacji na prostych typach. [jankazimirski](https://jankazimirski.pl/files/PWR.04.pdf)
    - Jednowątkowa pętla o dużym zakresie bez jakiegokolwiek podziału na równoległe jednostki pracy to dla GPU praktycznie brak roboty – jeden wątek lub jeden blok nie „rozbuja” setek rdzeni SM.  
    - 100000 iteracji to w świecie GPU jest mało; żeby 1660 miała co robić, zwykle mówimy o *milionach*–*dziesiątkach milionów* wątków lub dopasowaniu gridu tak, żeby occupancy była wysoka. [cs.put.poznan](http://www.cs.put.poznan.pl/rwalkowiak/pliki/2016/ows/projekt_pkg2016_ows.pdf)
    
    Stąd wynik: kompilujesz przez `nvcc`, ale faktycznie wykonuje się zwykły kod hosta, więc czas ~203 s się nie zmienia i wszystko jest na CPU. [cpp0x](https://cpp0x.pl/forum/temat/?id=6677)
    
    ## Co możesz realnie zrobić dalej
    
    Jeśli chcesz się **nauczyć CUDA** (a niekoniecznie „przyspieszyć pi z MPFR”), sensowna ścieżka:
    
    - Przepisz prostszą wersję algorytmu Archimedesa lub innej metody pi na `double` bez MPFR i zrównoleglij ją: jedna iteracja = jeden wątek.  
    - Zacznij od typowego przykładu wektorowego (dodawanie wektorów, mnożenie macierzy), potem wstrzyknij swój algorytm pi jako kernel, żeby zobaczyć różnicę między CPU i GPU przy rosnącym N. [bulldogjob](https://bulldogjob.pl/readme/pisanie-blyskawicznie-szybkiego-kodu-z-cuda)
    - Obserwuj `nvidia-smi` i profiluj `nvprof` / Nsight Systems, żeby zobaczyć occupancy, czasy kernel vs transfery. [cs.put.poznan](http://www.cs.put.poznan.pl/rwalkowiak/pliki/2019/pr/gpu_cuda_2019_pr.pdf)
    
    Jeżeli natomiast Twoim celem jest **wysokoprecyzyjne pi** (MPFR, setki cyfr):
    
    - Bardziej naturalne kierunki to optymalizacja CPU: wielowątkowość (OpenMP/pthreads), lepsze wykorzystanie cache, inne algorytmy (BBP, Chudnovsky, itd.), ew. SIMD, ale nadal na CPU. [cs.put.poznan](http://www.cs.put.poznan.pl/rwalkowiak/pliki/2016/ows/projekt_pkg2016_ows.pdf)
    - GPU raczej nie da Ci tu sensownego zysku przy tak dużej precyzji; przekładanie MPFR na CUDA jest możliwe teoretycznie, ale to duży projekt z wątpliwym ROI.

    Pozdr.
    Konto firmowe:
    IT SERVICES
    5 Military Road, Colchester, CO1 2AA | Strona WWW: https://www.komputerowiec.net
  • REKLAMA
  • #3 21930959
    MARTE.BEST
    Poziom 7  
    Posty: 141
    >>21930733 Dzięki. Jak widać złożony temat. Nie wiem czy sam to kiedyś ogarnę. Spróbuję przerobić jeszcze apkę CPIARCH, ale no zobaczymy... jak się cuda uda to się pochwalę. Pozdrawiam.
  • REKLAMA
  • #4 21930988
    Dżyszla
    Poziom 42  
    Posty: 7077
    Pomógł: 1095
    Ocena: 226
    Musisz zrozumieć parę rzeczy:
    1. Procesor GPU to nie jest architektura x86 ani żadna późniejsza opierająca się na tych założeniach.
    2. Procesor GPU jest znacznie WOLNIEJSZY od CPU! Jego wydajność wynika z tego, że potrafi równolegle wiele (szczególnie takich samych) obliczeń na raz. Jeśli piszesz coś, co pracuje jednowątkowo i nie ma możliwości na równoległość, to wykonanie takiego kodu na GPU będzie znacznie dłuższe.
    3. Wykonywanie kodu i zwracanie wyników przez GPU to nieco inne wykorzystanie, niż standardowe. Stąd właśnie powstały takie rozwiązania jak CUDA/OpenCL. I to właśnie od tych haseł musisz zacząć poznawanie tematu, jeśli chcesz wykonywać obliczenia na procesorze graficznym.

    Najczęściej właśnie wykorzystuje się frameworki, które za pośrednictwem funkcji poprzez zainstalowane i wspierające sterowniki graficzne pozwolą na napisanie kodu uruchamianego na GPU.
  • REKLAMA
  • #5 21931065
    MARTE.BEST
    Poziom 7  
    Posty: 141
    >>21930988 Z tego co napisałeś w 1 i 2 punkcie jakby częściowo zdawałem sobie sprawę.
    Dobrze, że przypomniałeś o OpenCL będę się tym też bawił - spróbuję.
    Na razie testuję inne algorytmy z reguły liniowe dla obliczeń Pi z użyciem MPFR.
  • #6 21931311
    Dżyszla
    Poziom 42  
    Posty: 7077
    Pomógł: 1095
    Ocena: 226
    To jeszcze jedno warto mieć na uwadze - GPU nastawiony jest na pracę na liczbach zmiennoprzecinkowych, ale obliczenia na dużych precyzujach wcale nie muszą korzystać z tego zapisu ze względu na wydajność, ale przede wszystkim błędy zaokrągleń. Stosuje się raczej obliczenia jednostki arytmetycznej - przewidywalność i szybkość jest atutem.
REKLAMA