2013-05-17 141 views
11

我一直在尋找方法來輕鬆多線程的一些簡單的分析代碼,因爲我注意到numpy它只使用一個核心,儘管事實上它應該是多線程的。爲什麼不是numpy.mean多線程?

我知道numpy被配置爲多核心,因爲我可以看到使用numpy.dot的測試使用了所有的核心,所以我只是將其重新實現爲點積,並且運行速度更快。是否有某種原因意味着它不能單獨運行?我發現對於較大的陣列也有類似的行爲,雖然比例接近2比我的例子中顯示的3。

我一直在閱讀一堆類似numpy速度問題的帖子,顯然它的方式比我想象的要複雜。任何見解都會有所幫助,因爲它更具可讀性並且代碼更少,所以我寧願使用mean,但我可能會切換到基於點的方式。

In [27]: data = numpy.random.rand(10,10) 

In [28]: a = numpy.ones(10) 

In [29]: %timeit numpy.dot(data,a)/10.0 
100000 loops, best of 3: 4.8 us per loop 

In [30]: %timeit numpy.mean(data,axis=1) 
100000 loops, best of 3: 14.8 us per loop 

In [31]: numpy.dot(data,a)/10.0 - numpy.mean(data,axis=1) 
Out[31]: 
array([ 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 
     0.00000000e+00, 1.11022302e-16, 0.00000000e+00, 
     0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 
     -1.11022302e-16]) 
+0

我想這是因爲numpy是用python編寫的,而python中的多線程因爲GIL而效率很低。 –

+3

@Riateche numpy的核心是用C編寫的。 – agf

+4

一定要做一個叫做'mean'的函數來使用,而不是在任何地方使用點的東西,所以如果'numpy.mean'改進,你可以稍後改變它。 – kwatford

回答

12

我一直在尋找各種方法來容易多線程我的一些簡單的分析代碼,因爲我numpy的它只是使用一個核心,儘管事實上,它應該是多線程已經注意到。

誰說它應該是多線程?

numpy主要是爲了儘可能快地在單獨的核心,如果你需要的話,以儘可能並行。但是你仍然需要並行化。

特別的,你可以在同一時間獨立的子對象進行操作,而緩慢的操作釋放GIL時可能的,雖然「可能的情況下」未必是遠遠不夠的。此外,numpy對象旨在儘可能方便地在流程之間共享或傳遞,以便於使用multiprocessing

但是也有一些自動並行化一些專門的方法,但是大多數的核心方法都沒有。特別是,dot在可能的情況下在BLAS之上實現,BLAS在大多數平臺上自動並行化,但mean以純C代碼實現。

詳見Parallel Programming with numpy and scipy


那麼,你怎麼知道哪些方法是並行的,哪些不是?而且,在那些不是,你怎麼知道哪些可以很好地手動線程,哪些需要多處理?

沒有很好的答案。你可以做出有根據的猜測(X看起來好像可能是在ATLAS之上實現的,我的ATLAS副本是隱含地線程化的),或者你可以閱讀源代碼。

但通常情況下,最好的做法是嘗試並測試。如果代碼使用一個核心的100%和其他的0%,則添加手動線程。如果現在使用一個核心的100%和其他10%的核心,並且幾乎沒有更快的運行速度,請將多線程更改爲多處理。 (幸運的是,Python使得這很容易,尤其是如果你使用concurrent.futures的Executor類或者multiprocessing的Pool類。但是你仍然經常需要考慮一下,並且測試共享與傳遞的相對成本,如果你有大陣列)。

此外,正如kwatford指出的那樣,僅僅因爲某些方法似乎並不隱含並行並不意味着它不會在下一版本的numpy或下一版本的BLAS中並行,或者在不同的平臺上,甚至在安裝了稍微不同的東西的機器上。所以,準備重新測試。並執行諸如my_mean = numpy.mean之類的操作,然後隨處使用my_mean,因此您只需將一行更改爲my_mean = pool_threaded_mean即可。

6

基本上,因爲BLAS庫具有優化的點積,他們可以很容易地要求dot即本質爲並行。他們承認他們可以延伸numpy來平行其他行動,但選擇不走這條路線。然而,他們給出了關於如何並行化你的numpy代碼的幾個技巧(基本上是爲了在N個核心之間劃分工作(例如,N = 4),將你的陣列分成N個子陣列並且將每個子陣列的作業發送到它自己的線程,然後結合你的結果)。

http://wiki.scipy.org/ParallelProgramming

使用並行原語

一個numpy的的巨大優勢是,你可以表達陣列操作非常乾淨。例如,要計算矩陣A的產品和矩陣B,你只是做:

>>> C = numpy.dot(A,B)

這不僅是簡單明瞭的讀寫,因爲numpy的知道你想要做一個矩陣點產品,它可以使用作爲「BLAS」(基本線性代數子程序)的一部分獲得的優化實現。這通常是一個經過仔細調整的庫,可以利用緩存和彙編實現在硬件上儘可能快地運行。但是現在許多架構都有一個BLAS,它也可以利用多核機器。如果你的numpy/scipy是使用其中一種編譯的,那麼dot()將被並行計算(如果這更快),而你沒有做任何事情。對於其他矩陣運算也是如此,如反演,奇異值分解,行列式等等。例如,開源庫ATLAS允許編譯時選擇並行度(線程數)。來自英特爾的專有MKL庫提供了在運行時選擇並行度的可能性。還有GOTO庫允許運行時選擇並行性水平。這是一個商業產品,但源代碼免費分發給學術用途。

最後,SciPy的/ numpy的不併行操作,如

>>> A = B + C

>>> A = numpy.sin(B)

>>> A = scipy.stats.norm.isf(B)

這些操作順序運行,不採取任何多核機器的優勢(見下文)。原則上,這可以在沒有太多工作的情況下改變。 OpenMP是C語言的擴展,它允許編譯器爲適當註釋的循環(和其他事物)生成並行代碼。如果有人在numpy(可能是scipy)中坐下並註釋了一些核心循環,並且如果有人在打開OpenMP的情況下編譯了numpy/scipy,則上述三項都將自動並行運行。當然,實際上人們會希望有一些運行時控制 - 例如,如果有人計劃在同一個多處理器機器上運行多個作業,可能需要關閉自動並行化。

+0

+ 1。唯一的問題是,他們不能保證任何操作釋放GIL和/或在部分子對象上獨立操作,只是爲了讓所有東西都可以線程化。所以你幾乎必須測試手動線程,跨越你的手指,並準備在必要時回退到多處理(並且,對於一個足夠大的陣列,你還必須測試共享與傳遞給IPC)。幸運的是,Python在需要時很容易回退到多處理,所以這不是什麼大問題。 – abarnert

相關問題