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.