使Python代碼的速度提高1000倍


使Python代碼的速度提高1000倍


在這篇文章中,我將分享您可能在日常腳本中使用的3種Python效率技術,以及如何度量2種解決方案之間的性能改進。我們開始吧!

如何比較兩種候選解決方案的性能?

入門Python其實很容易,但是我們要去堅持學習,每一天堅持很困難,我相信很多人學了一個星期就放棄了,為什麼呢?其實沒有好的學習資料給你去學習,你們是很難堅持的,這是小編收集的Python入門學習資料關注,轉發,私信小編“01”,即可免費領取!希望對你們有幫助

使Python代碼的速度提高1000倍


性能可以參考解決方案中的許多不同因素(例如執行時間、CPU使用情況、內存使用情況等)。不過,在這篇文章中,我們將重點關注執行時間。

新解決方案的執行時間的改進可以簡單地計算為進行除法。也就是說,我們將把舊的(或非優化的)解決方案的執行時間除以新的(或優化的)解決方案:TEST/tNew。這個指標通常被稱為加速比...例如,如果我們的加速比因子為2,那麼我們改進的解決方案所需的時間將是原解決方案的一半。

為了比較我們的函數的性能,我們將創建一個接收這兩個函數的函數,計算它們的執行時間,並計算獲得的加速比:

<code>import timedef compute_speedup(slow_func, opt_func, func_name, tp=None):
x = range(int(1e5))
if tp: x = list(map(tp, x)) slow_start = time.time()
slow_func(x)
slow_end = time.time()
slow_time = slow_end - slow_start opt_start = time.time()
opt_func(x)
opt_end = time.time()
opt_time = opt_end - opt_start speedup = slow_time/opt_time
print('{} speedup: {}'.format(func_name, speedup))/<code>

為了獲得重要的結果,我們將使用一個相對較大的數組(100000元素),並將其作為參數傳遞給這兩個函數。然後,我們將使用時間模塊計算執行時間,並最終交付所獲得的加速比。

注意,我們還傳遞了一個可選參數,允許我們更改List元素的類型。

1.使用地圖功能

當我們需要對列表中的每個元素進行操作時,我們通常可以這樣做:我們使用列表理解遍歷列表,並對當前元素進行工作:

<code>def slow_map(x):
[str(n) for n in x]/<code>

然而,在許多情況下,您可能更喜歡使用Python內置映射函數,它將相同的操作應用於列表中的每個元素。最重要的是,可以簡單地按以下方式實施:

<code>def opt_map(x):
map(str, x)/<code>

是時候檢查一下我們已經改進了多少執行時間!運行計算加速比函數如下:

<code>compute_speedup(slow_map, opt_map, 'map')/<code>

我得到了1099的加速比。這意味著,如果我們有一個使用map函數運行1秒的數組,那麼使用列表理解運行大約需要18分鐘!顯然,如果我們將其與for循環實現進行比較,速度會更高。既然您有了驗證它的工具,我將把它作為一個小練習留給您!

2.避免重新評價職能

每當您發現自己對循環塊中的元素重複使用相同的函數時,例如:

<code>y = []
for n in x:
y.append(n)
y.append(n**2)
y.append(n**3)/<code>

…或者只在循環塊中使用這種函數一次,但是在一個大列表上使用,如下所示:

<code>def slow_loop(x):
y = []
for n in x:
y.append(n) = []/<code>

…您可以利用另一種優化技術。這個場景假設您不能使用map(如果可以的話,可以使用它:加速比要高得多)。

如果以前將函數作為變量保存並在循環塊中重用,則可以節省重新評估函數的成本。下面的片段顯示了這種行為:

<code>def opt_loop(x):
y = []
append = y.append
for n in x:
append(n)/<code>

注意,如果需要將當前元素追加到不同的列表中,則必須為每個列表的append函數創建一個新變量。

讓我們使用計算加速比檢查加速比:

<code>compute_speedup(slow_loop, opt_loop, 'loop')/<code>

在這種情況下,我得到了2.07的加速比!考慮到實現這一技術所需的工作量不多,我認為這並不算太糟。

3.避免將字符串與+運算符連接起來

您可能發現的一個常見情況是,必須形成一個具有多個子部分的字符串。Python有一個方便的+操作符,它允許我們以以下方式輕鬆地連接字符串:

<code>def slow_join(x):
s = ''
for n in x:
s += n/<code>

儘管對我們來說它似乎是一種乾淨的方法,但是Python的字符串被創建為不可變,因此不能被修改。這意味著每次我們使用+運算符時,Python實際上都是基於子字符串和返回新字符串創建一個新字符串。考慮一下,在我們的例子中,這個操作將被執行100000次。

這種方法顯然是有代價的,我們可能會找到一種更便宜的解決方案加入(),如以下示例所示:

<code>def opt_join(x): 

s = ''.join(x)/<code>

此解決方案接受子字符串數組,並將它們與空字符串分隔符連接起來。讓我們檢查一下我們的性能改進:

<code>compute_speedup(slow_join, opt_join, 'join', tp=str)/<code>

我的加速比是7.25!再說一遍,我們不需要做任何重大的改變來獲得這樣的改進。


分享到:


相關文章: