簡體   English   中英

numpy矩陣乘法的奇怪性能結果

[英]Strange performance results for numpy matrix multiplication

最近,我發現了一種情況,其中與numpy相乘的矩陣乘法顯示出非常奇怪的性能(至少對我而言)。 為了說明這一點,我創建了此類矩陣的示例以及一個簡單的腳本來演示計時。 兩者都可以從repo下載,我在這里不包括腳本,因為沒有數據就沒什么用了。

該腳本使用dot函數和einsum以不同的方式將兩對矩陣相乘(每對矩陣的shapedtype ,只是數據不同)。 實際上,我注意到了一些異常情況:

  • 第一對( A * B )的乘法比第二對( C * D )快得多。
  • 當我將所有矩陣都轉換為float64 ,兩對的時間變得相同:比乘A * B時間更長,但比C * D所需的時間短。
  • 這些效果對於einsum (據我理解為numpy實現)和dot (在我的計算機上使用BLAS)都保持不變。 為了完整起見,此腳本在我的筆記本電腦上的輸出:
 With np.dot: A * B: 0.142910003662 s C * D: 4.9057161808 s A * D: 0.20524597168 s C * B: 4.20220398903 s A * B (to float32): 0.156805992126 s C * D (to float32): 5.11792707443 s A * B (to float64): 0.52608704567 s C * D (to float64): 0.484733819962 s A * B (to float64 to float32): 0.255760908127 s C * D (to float64 to float32): 4.7677090168 s With einsum: A * B: 0.489732980728 s C * D: 7.34477996826 s A * D: 0.449800014496 s C * B: 4.05954909325 s A * B (to float32): 0.411967992783 s C * D (to float32): 7.32073783875 s A * B (to float64): 0.80580997467 s C * D (to float64): 0.808521032333 s A * B (to float64 to float32): 0.414498090744 s C * D (to float64 to float32): 7.32472801208 s 

如何解釋這樣的結果,以及如何像A * B一樣更快地將C * D相乘?

您看到的速度下降是由於涉及非正規數的計算。 當使用非正規輸入或輸出執行算術運算時,許多處理器的速度要慢得多。 有一對夫婦的現有StackOverflow的問題,這是相關的:看到這個相關的C#的問題 (特別是答案埃里克Postpischil),並且這個答案為C ++的問題以獲取更多信息。

在您的特定情況下,矩陣C (具有float32 )包含多個次正規數。 對於單精度浮點,次法線/法線邊界為2^-126或大約1.18e-38 這是我對C的看法:

>>> ((0 < abs(C)) & (abs(C) < 2.0**-126)).sum()  # number of subnormal entries
44694
>>> C.size
682450

因此,約有6.5%的C項是次正規的,這足以減慢C*BC*D乘法。 相反, AB不在次法線邊界附近:

>>> abs(A[A != 0]).min()
4.6801152e-12
>>> abs(B[B != 0]).min()
4.0640174e-07

因此, A*B矩陣乘法所涉及的中間值都不是次正規的,並且不施加速度損失。

至於您的問題的第二部分,我不確定要提出什么建議。 如果您盡力而為,並且使用的是x64 / SSE2(而不是x87 FPU),則可以從Python設置“刷新為零”和“非規范為零”標志。 有關基於ctypes的粗略且不可移植的hack,請參見此答案 如果您真的想遵循這條路線,最好編寫一個自定義C擴展名來做到這一點。

相反,我很想嘗試縮放C以使其完全處於正常范圍內(並將C*D的各個乘積也帶到正常范圍內),但是如果C值也等於C ,則可能無法實現。浮點范圍的上限。 另外,可以簡單地用零代替C的微小值,但是結果導致的精度損失是否顯着和/或可以接受將取決於您的應用程序。

Mark Dickinson已經回答了您的問題,但是為了好玩,請嘗試以下操作:

Cp = np.array(list(C[:,0]))
Ap = np.array(list(A[:,0]))

這樣可以消除拼接延遲,並確保陣列在內存中相似。

%timeit Cp * Cp   % 34.9 us per loop
%timeit Ap * Ap   % 3.59 us per loop

哎呦。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM