[英]How to use multi threading on this for loop to decrease the execution time?
[英]Rewriting a for loop in pure NumPy to decrease execution time
我最近詢問了如何為科學應用程序優化Python循環 ,並在NumPy中獲得了一種優秀,智能的重新編碼方式,這使我的執行時間減少了大約100倍 !
但是, B
值的計算實際上嵌套在其他幾個循環中,因為它是在規則的位置網格上進行計算的。 有沒有類似的智能NumPy重寫來削減這個程序的時間?
我懷疑這部分的性能提升不太明顯,並且缺點可能是不可能向用戶報告計算進度,結果無法寫入輸出文件,直到計算的結束,並且可能在一個巨大的步驟中這樣做會產生內存影響嗎? 有可能繞過這些嗎?
import numpy as np
import time
def reshape_vector(v):
b = np.empty((3,1))
for i in range(3):
b[i][0] = v[i]
return b
def unit_vectors(r):
return r / np.sqrt((r*r).sum(0))
def calculate_dipole(mu, r_i, mom_i):
relative = mu - r_i
r_unit = unit_vectors(relative)
A = 1e-7
num = A*(3*np.sum(mom_i*r_unit, 0)*r_unit - mom_i)
den = np.sqrt(np.sum(relative*relative, 0))**3
B = np.sum(num/den, 1)
return B
N = 20000 # number of dipoles
r_i = np.random.random((3,N)) # positions of dipoles
mom_i = np.random.random((3,N)) # moments of dipoles
a = np.random.random((3,3)) # three basis vectors for this crystal
n = [10,10,10] # points at which to evaluate sum
gamma_mu = 135.5 # a constant
t_start = time.clock()
for i in range(n[0]):
r_frac_x = np.float(i)/np.float(n[0])
r_test_x = r_frac_x * a[0]
for j in range(n[1]):
r_frac_y = np.float(j)/np.float(n[1])
r_test_y = r_frac_y * a[1]
for k in range(n[2]):
r_frac_z = np.float(k)/np.float(n[2])
r_test = r_test_x +r_test_y + r_frac_z * a[2]
r_test_fast = reshape_vector(r_test)
B = calculate_dipole(r_test_fast, r_i, mom_i)
omega = gamma_mu*np.sqrt(np.dot(B,B))
# write r_test, B and omega to a file
frac_done = np.float(i+1)/(n[0]+1)
t_elapsed = (time.clock()-t_start)
t_remain = (1-frac_done)*t_elapsed/frac_done
print frac_done*100,'% done in',t_elapsed/60.,'minutes...approximately',t_remain/60.,'minutes remaining'
你可以做的一件顯而易見的事就是更換線路
r_test_fast = reshape_vector(r_test)
同
r_test_fast = r_test.reshape((3,1))
可能不會對性能產生任何重大影響,但無論如何使用numpy內置驅動器而不是重新發明輪子是有意義的。
一般來說,正如您現在可能已經注意到的那樣,優化numpy的技巧是在numpy全數組操作的幫助下表達算法,或者至少使用切片而不是迭代python代碼中的每個元素。 傾向於阻止這種“向量化”的是所謂的循環攜帶依賴性,即每次迭代依賴於先前迭代的結果的循環。 簡要地看一下你的代碼,你沒有這樣的東西,應該可以很好地矢量化你的代碼。
編輯:一個解決方案
我沒有證實這是正確的,但應該讓你知道如何處理它。
首先, 使用我們將使用的cartesian()函數 。 然后
def calculate_dipole_vect(mus, r_i, mom_i):
# Treat each mu sequentially
Bs = []
omega = []
for mu in mus:
rel = mu - r_i
r_norm = np.sqrt((rel * rel).sum(1))
r_unit = rel / r_norm[:, np.newaxis]
A = 1e-7
num = A*(3*np.sum(mom_i * r_unit, 0)*r_unit - mom_i)
den = r_norm ** 3
B = np.sum(num / den[:, np.newaxis], 0)
Bs.append(B)
omega.append(gamma_mu * np.sqrt(np.dot(B, B)))
return Bs, omega
# Transpose to get more "natural" ordering with row-major numpy
r_i = r_i.T
mom_i = mom_i.T
t_start = time.clock()
r_frac = cartesian((np.arange(n[0]) / float(n[0]),
np.arange(n[1]) / float(n[1]),
np.arange(n[2]) / float(n[2])))
r_test = np.dot(r_frac, a)
B, omega = calculate_dipole_vect(r_test, r_i, mom_i)
print 'Total time for vectorized: %f s' % (time.clock() - t_start)
好吧,在我的測試中,這實際上比我開始的基於循環的方法稍慢。 問題是,在問題的原始版本中,它已經在形狀數組(20000,3)上進行了全數組運算的矢量化,因此任何進一步的矢量化都不會帶來更多的好處。 事實上,如上所述,它可能會使性能惡化,可能是由於大型臨時陣列。
如果您對代碼進行概要分析 ,您將看到99%的運行時間都在calculate_dipole
因此減少此循環的時間實際上不會顯着縮短執行時間。 如果你想讓它更快,你仍然需要專注於calculate_dipole。 我在此嘗試了針對calculate_dipole
Cython代碼,並在整個時間內減少了大約2倍。 可能還有其他方法可以改進Cython代碼。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.