hpc:gpu
Skirtumai
Čia matote skirtumus tarp pasirinktos versijos ir esamo dokumento.
Both sides previous revisionPrevious revision | Next revisionBoth sides next revision | ||
hpc:gpu [2022/01/31 16:46] – linp | hpc:gpu [2022/01/31 17:11] – linp | ||
---|---|---|---|
Linija 77: | Linija 77: | ||
</ | </ | ||
+ | |||
+ | |||
+ | ====== GPU išnaudojimas lygiagretinant kodą su Numba biblioteka ====== | ||
+ | |||
+ | === Įvadas į CUDA Python su Numba === | ||
+ | |||
+ | CUDA skaičiavimo biblioteka įgalina programų pagreitinimą, | ||
+ | |||
+ | **Numba** yra " | ||
+ | |||
+ | |||
+ | === Pavyzdžiai darbas su Numba: Pvz. 1 === | ||
+ | |||
+ | Pavydžiui, realizuokime Pitagorinę sudėtį: | ||
+ | |||
+ | https:// | ||
+ | |||
+ | <code shell> | ||
+ | # importuojame jit kompiliatorių | ||
+ | from numba import jit | ||
+ | import numpy as np | ||
+ | import math | ||
+ | |||
+ | # Sintaksė @jit ekvivalentu užrašui `hypot = jit(hypot)`. | ||
+ | @jit | ||
+ | def hypot(x, y): | ||
+ | x = abs(x); | ||
+ | y = abs(y); | ||
+ | t = min(x, y); | ||
+ | x = max(x, y); | ||
+ | t = t / x; | ||
+ | return x * math.sqrt(1+t*t) | ||
+ | |||
+ | |||
+ | # Bandome | ||
+ | hypot(3.0, 4.0) | ||
+ | |||
+ | |||
+ | # Nesukompiliuotos su *jit* funkcijos iškvietimas | ||
+ | ypot.py_func(3.0, | ||
+ | |||
+ | </ | ||
+ | |||
+ | Gauti metodų vykdymo laikus galėtume atitinkamai pvz: | ||
+ | |||
+ | <code shell> | ||
+ | timeit hypot.py_func(3.0, | ||
+ | |||
+ | timeit hypot(3.0, 4.0) | ||
+ | </ | ||
+ | |||
+ | === Pavyzdžiai darbas su Numba: Pvz. 2 === | ||
+ | |||
+ | Parašykime programą pi vertinimui Monte Karlo metodu (žr. https:// | ||
+ | |||
+ | <code shell> | ||
+ | from numba import jit | ||
+ | import random | ||
+ | |||
+ | @jit | ||
+ | def monte_carlo_pi(nsamples): | ||
+ | acc = 0 | ||
+ | for i in range(nsamples): | ||
+ | x = random.random() | ||
+ | y = random.random() | ||
+ | if (x**2 + y**2) < 1.0: | ||
+ | acc += 1 | ||
+ | return 4.0 * acc / nsamples | ||
+ | |||
+ | |||
+ | |||
+ | import matplotlib.pyplot as plt | ||
+ | |||
+ | |||
+ | nsamples = 1000000 | ||
+ | |||
+ | n = [2**i for i in range(0, 20)] | ||
+ | pi_values = [monte_carlo_pi(i) for i in n] | ||
+ | |||
+ | plt.plot(n, pi_values) | ||
+ | plt.axhline(y=np.pi, | ||
+ | plt.xscale(' | ||
+ | plt.xlabel(" | ||
+ | plt.ylabel(" | ||
+ | </ | ||
+ | |||
+ | |||
+ | Gauti metodų vykdymo laikus galėtume atitinkamai pvz: | ||
+ | |||
+ | <code shell> | ||
+ | timeit monte_carlo_pi(nsamples) | ||
+ | |||
+ | timeit monte_carlo_pi.py_func(nsamples) | ||
+ | </ | ||
+ | |||
+ | |||
+ | === Kaip Numba veikia? | ||
+ | |||
+ | Numba kompiliatorius atsivželgia į duomenų tipus ir optimizuoja tarpinius skaičiavimu (angl. Intermediate representation (IR)) | ||
+ | |||
+ | |||
+ | ### **Numba** optimizuojant kodą skirtą GPU naudojant **NumPy** universalias funkcijas (ufuncs) | ||
+ | Toliau laikysime GPU programavimo aprėpyje jog dirbsime su NumPy universaliomis funkcijas (arba ufuncs) - t.y. standartinės bibliotekos funkcijos žr. dokumentaciją: | ||
+ | |||
+ | === Pavyzdžiai darbas su Numba: Pvz. 3 === | ||
+ | |||
+ | <code shell> | ||
+ | |||
+ | import numpy as np | ||
+ | |||
+ | a = np.array([1, | ||
+ | b = np.array([10, | ||
+ | |||
+ | np.add(a, b) | ||
+ | |||
+ | |||
+ | # Universalios funkcijos veikia su vektoriais ir skaičiais | ||
+ | np.add(a, 100) | ||
+ | |||
+ | |||
+ | # Matriciniu atveju, vektoriai taikomi sąrašo elementams t.y. eilutėms | ||
+ | c = np.arange(4*4).reshape((4, | ||
+ | print(' | ||
+ | |||
+ | np.add(b, c) | ||
+ | </ | ||
+ | |||
+ | |||
+ | |||
+ | === Universalių funkcijų kūrimas ir optimizavimas | ||
+ | |||
+ | |||
+ | Svarbu suprasti, jog norint kurti universalias fukcijas, tokias turime ir naudoti. Šiuo atveju, tam mum bus reikalinkas metodas *vectorize*. | ||
+ | |||
+ | Šiame pačiame pirmame pavyzdyje naudosime *vectorize*, | ||
+ | |||
+ | |||
+ | |||
+ | <code shell> | ||
+ | from numba import vectorize | ||
+ | |||
+ | @vectorize | ||
+ | def add_ten(num): | ||
+ | return num + 10 | ||
+ | |||
+ | nums = np.arange(10) | ||
+ | nums | ||
+ | |||
+ | add_ten(nums) | ||
+ | |||
+ | # Dabar norint išnaudoti **GPU** optimizavimą mum reikia | ||
+ | @vectorize([' | ||
+ | def add_ufunc(x, | ||
+ | return x + y | ||
+ | |||
+ | add_ufunc(a, | ||
+ | </ | ||
+ | |||
+ | |||
+ | Gauti metodų vykdymo laikus galėtume atitinkamai pvz: | ||
+ | |||
+ | <code shell> | ||
+ | timeit np.add(b, c) # NumPy - CPU | ||
+ | |||
+ | timeit add_ufunc(b, | ||
+ | </ | ||
+ | |||
+ | # Ant CPU veikia greičiau :) | ||
+ | # 1. duomenys per maži | ||
+ | # 2. operacijos primityvios | ||
+ | # 3. kopijuojame duomenis į GPU | ||
+ | # 4. naudojame didelius duomenis (int64) | ||
+ | |||
+ | |||
+ | === Pavyzdžiai darbas su Numba: Pvz. 4 === | ||
+ | |||
+ | Panagrinėkime sudėtingesnį atvejį f(x|\mu, \sigma) = \frac{e^{-((x-\mu)/ | ||
+ | |||
+ | <code shell> | ||
+ | |||
+ | |||
+ | import math | ||
+ | |||
+ | SQRT_2PI = np.float32((2*math.pi)**0.5) | ||
+ | |||
+ | @vectorize([' | ||
+ | def gaussian_pdf(x, | ||
+ | return math.exp(-0.5 * ((x - mean) / sigma)**2) / (sigma * SQRT_2PI) | ||
+ | |||
+ | |||
+ | |||
+ | import numpy as np | ||
+ | |||
+ | x = np.random.uniform(-3, | ||
+ | x = np.sort(x) | ||
+ | |||
+ | mean = np.float32(0.0) | ||
+ | sigma = np.float32(1.0) | ||
+ | |||
+ | |||
+ | f = gaussian_pdf(x, | ||
+ | |||
+ | |||
+ | |||
+ | plt.plot(x, f) | ||
+ | plt.xlabel(' | ||
+ | plt.ylabel(' | ||
+ | </ | ||
+ | |||
+ | Gauti metodų vykdymo laikus galėtume atitinkamai pvz: | ||
+ | |||
+ | <code shell> | ||
+ | timeit gaussian_pdf(x, | ||
+ | |||
+ | import scipy.stats | ||
+ | norm_pdf = scipy.stats.norm | ||
+ | timeit norm_pdf.pdf(x, | ||
+ | </ | ||
+ | |||
+ | |||
+ | |||
+ | |||
+ | === Pavyzdžiai darbas su Numba: Pvz. 5 === | ||
+ | |||
+ | Tais atvejais, kai norime spartinti vektorinius skaičiavimus, | ||
+ | |||
+ | |||
+ | <code shell> | ||
+ | |||
+ | from numba import cuda | ||
+ | |||
+ | @cuda.jit(device=True) | ||
+ | def polar_to_cartesian(rho, | ||
+ | x = rho * math.cos(theta) | ||
+ | y = rho * math.sin(theta) | ||
+ | return x, y | ||
+ | |||
+ | @vectorize([' | ||
+ | def polar_distance(rho1, | ||
+ | x1, y1 = polar_to_cartesian(rho1, | ||
+ | x2, y2 = polar_to_cartesian(rho2, | ||
+ | | ||
+ | return ((x1 - x2)**2 + (y1 - y2)**2)**0.5 | ||
+ | |||
+ | |||
+ | n = 1000000 | ||
+ | rho1 = np.random.uniform(0.5, | ||
+ | theta1 = np.random.uniform(-np.pi, | ||
+ | rho2 = np.random.uniform(0.5, | ||
+ | theta2 = np.random.uniform(-np.pi, | ||
+ | |||
+ | |||
+ | polar_distance(rho1, | ||
+ | </ | ||
+ | |||
+ | |||
+ | === Plačiau apie GPU išnaudojimą | ||
+ | |||
+ | |||
+ | * [[https:// | ||
+ | |||
+ | * [[https:// |
hpc/gpu.txt · Keista: 2022/10/30 13:54 vartotojo linp