一、對於python的基礎介紹
Python是一種高效的動態編程語言,廣泛用於科學,工程和數據分析應用程序。。影響python普及的因素有很多,包括干凈,富有表現力的語法和標准數據結構,全面的“電池包含”標准庫,優秀的文檔,廣泛的圖書館和工具生態系統,專業支持的可用性以及大而開放社區。但也許最重要的是,像Python這樣的動態類型化解釋語言能夠實現高效率。Python靈活靈活,使其成為快速原型設計的理想語言,同時也是構建完整系統的理想語言。
但是Python的最大優勢也可能是它最大的弱點:它的靈活性和無類型的高級語法可能導致數據和計算密集型程序的性能不佳。
二、numba--高性能高效率的計算
在這篇文章中,我將向您介紹Numba,一個來自Anaconda的Python編譯器,可以編譯Python代碼,以便在支持CUDA的GPU或多核CPU上執行。由於Python通常不是編譯語言,您可能想知道為什么要使用Python編譯器。答案當然是運行本機編譯代碼比運行動態解釋代碼快許多倍。 Numba允許您為Python函數指定類型簽名,它可以在運行時進行編譯(這是“即時”或JIT編譯)。 Numba動態編譯代碼的能力意味着您不會放棄Python的靈活性。這是向高效率編程和高性能計算提供理想組合的重要一步。
使用Numba,現在可以編寫標准的Python函數並在支持CUDA的GPU上運行它們。 Numba專為面向陣列的計算任務而設計,就像廣泛使用的NumPy庫一樣。面向陣列的計算任務中的數據並行性非常適合GPU等加速器。 Numba了解NumPy數組類型,並使用它們生成有效的編譯代碼,以便在GPU或多核CPU上執行。所需的編程工作可以像添加函數裝飾器一樣簡單,以指示Numba為GPU編譯。例如,以下代碼中的@vectorize裝飾器在運行時生成標量函數Add的已編譯矢量化版本,以便它可用於在GPU上並行處理數據數組
imort numpy as np from numba import vectorize @vectorize(['float32(float32, float32)'], target='cuda') def Add(a, b): return a + b # Initialize arrays N = 100000 A = np.ones(N, dtype=np.float32) B = np.ones(A.shape, dtype=A.dtype) C = np.empty_like(A, dtype=A.dtype) # Add arrays on GPU C = Add(A, B)
要在CPU上編譯和運行相同的函數,我們只需將目標更改為“cpu”,從而在CPU上編譯的矢量化C代碼級別上產生性能。 這種靈活性可幫助您生成更多可重用的代碼,並允許您在沒有GPU的計算機上進行開發。
三、用於Python的GPU加速庫
CUDA並行計算平台的優勢之一是其廣泛的GPU加速庫。 Numba團隊的另一個名為pyculib的項目為CUDA cuBLAS(密集線性代數),cuFFT(快速傅立葉變換)和cuRAND(隨機數生成)庫提供了Python接口。 許多應用程序只需使用這些庫就可以獲得顯着的加速,而無需編寫任何特定於GPU的代碼。 例如,以下代碼使用“XORWOW”偽隨機數生成器在GPU上生成一百萬個均勻分布的隨機數。
import numpy as np from pyculib import rand as curand prng = curand.PRNG(rndtype=curand.PRNG.XORWOW) rand = np.empty(100000) prng.uniform(rand) print rand[:10]
四、與CUDA Python的大規模並行性
Anaconda(前身為Continuum Analytics)認識到,在某些計算上實現大幅加速需要一個更具表現力的編程接口,對並行性的控制要比庫和自動循環矢量化所能提供的更詳細。 因此,Numba具有另一組重要功能,可以構成非正式名稱為“CUDA Python”的功能。 Numba公開CUDA編程模型,就像在CUDA C / C ++中一樣,但是使用純python語法,這樣程序員就可以創建自定義的,調優的並行內核,而不會留下Python的舒適和優勢。 Numba的CUDA JIT(可通過裝飾器或函數調用獲得)在運行時編譯CUDA Python函數,專門針對您使用的類型,並且其CUDA Python API提供對數據傳輸和CUDA流的顯式控制,以及其他功能
下面的代碼示例使用簡單的Mandelbrot設置內核演示了這一點。 請注意,mandel_kernel函數使用Numba提供的cuda.threadIdx,cuda.blockIdx,cuda.blockDim和cuda.gridDim結構來計算當前線程的全局X和Y像素索引。 與其他CUDA語言一樣,我們通過在函數名稱和參數列表之間插入一個“執行配置”(CUDA-代表用於運行內核的線程數和線程數)來啟動內核:mandel_kernel [griddim,blockdim]( - 2.0,1.0,-1.0,1.0,d_image,20)。 您還可以看到使用to_host和to_device API函數將數據復制到GPU或從GPU復制數據。
@cuda.jit(device=True) def mandel(x, y, max_iters): """ Given the real and imaginary parts of a complex number, determine if it is a candidate for membership in the Mandelbrot set given a fixed number of iterations. """ c = complex(x, y) z = 0.0j for i in range(max_iters): z = z*z + c if (z.real*z.real + z.imag*z.imag) >= 4: return i return max_iters @cuda.jit def mandel_kernel(min_x, max_x, min_y, max_y, image, iters): height = image.shape[0] width = image.shape[1] pixel_size_x = (max_x - min_x) / width pixel_size_y = (max_y - min_y) / height startX = cuda.blockDim.x * cuda.blockIdx.x + cuda.threadIdx.x startY = cuda.blockDim.y * cuda.blockIdx.y + cuda.threadIdx.y gridX = cuda.gridDim.x * cuda.blockDim.x; gridY = cuda.gridDim.y * cuda.blockDim.y; for x in range(startX, width, gridX): real = min_x + x * pixel_size_x for y in range(startY, height, gridY): imag = min_y + y * pixel_size_y image[y, x] = mandel(real, imag, iters) gimage = np.zeros((1024, 1536), dtype = np.uint8) blockdim = (32, 8) griddim = (32,16) start = timer() d_image = cuda.to_device(gimage) mandel_kernel[griddim, blockdim](-2.0, 1.0, -1.0, 1.0, d_image, 20) d_image.to_host() dt = timer() - start print "Mandelbrot created on GPU in %f s" % dt imshow(gimage) On a server with an NVIDIA Tesla P100 GPU and an Intel Xeon E5-2698 v3 CPU, this CUDA Python Mandelbrot code runs nearly 1700 times faster than the pure Python version. 1700x may seem an unrealistic speedup, but keep in mind that we are comparing compiled, parallel, GPU-accelerated Python code to interpreted, single-threaded Py
在配備NVIDIA Tesla P100 GPU和Intel Xeon E5-2698 v3 CPU的服務器上,此CUDA Python Mandelbrot代碼的運行速度比純Python版快近1700倍。 1700x可能看起來不切實際,但請記住,我們正在將編譯的,並行的,GPU加速的Python代碼與CPU上的解釋的單線程Python代碼進行比較。、
四、 立即開始使用Numba
Numba為Python開發人員提供了一種輕松進入GPU加速計算的途徑,以及使用日益復雜的CUDA代碼並使用最少的新語法和術語的途徑。您可以從簡單的函數裝飾器開始,自動編譯您的函數,或使用pyculib公開的強大的CUDA庫。隨着您對並行編程概念的理解以及當您需要對並行線程進行表達和靈活控制時,CUDA可用,而無需您在第一天進入。
Numba是一個獲得BSD許可的開源項目,它本身在很大程度上依賴於LLVM編譯器的功能。 Numba的GPU后端使用基於LLVM的NVIDIA編譯器SDK。 CUDA庫周圍的pyculib包裝器也是開源和BSD許可的。
要開始使用Numba,第一步是下載並安裝Anaconda Python發行版,這是一個“完全免費的企業級Python發行版,用於大規模數據處理,預測分析和科學計算”,其中包括許多流行的軟件包(Numpy, Scipy,Matplotlib,iPython等)和“conda”,一個強大的包管理器。安裝Anaconda后,鍵入conda install numba cudatoolkit pyculib安裝所需的CUDA軟件包。然后在ContinuumIO github存儲庫上查看CumbA的Numba教程。我還建議您查看Anaconda博客上的Numba帖子。