測試代碼的執行時間:%time和%timeit

對於規模更大、運行時間更長的數據分析應用程序,你可能會希望測試一下各個部分或函數調用或語句的執行時間。你可能會希望瞭解某個複雜計算過程中到底是哪些函數佔用的時間最多。幸運的是,在開發和測試代碼的過程中,IPython能夠讓你輕鬆得到這些信息。使用內置的time模塊及其time.clock和time.time函數手工測試代碼執行時間是一件令人煩悶的事情,因爲你必須編寫許多一模一樣的了無生趣的公式化代碼:

import time
start = time.time()
for i in range(iterations):
    # 這裏放一些待執行的代碼
    elapsed_per = (time.time() - start) / iterations

由於這是一個非常常用的功能,所以IPython專門提供了兩個魔術函數(%time和%timeit)以便自動完成該過程。%time一次執行一條語句,然後報告總體執行時間。假設我們有一大堆字符串,希望對幾個“能夠選出具有特殊前綴的字符串”的函數進行比較。下面是一個擁有60萬字符串的數組,以及兩個不同的“能夠選出其中以foo開頭的字符串”的方法:

# 一個非常大的字符串數組
strings = ['foo', 'foobar', 'baz', 'qux', 'python', 'Guido Van Rossum'] * 100000
method1 = [x for x in strings if x.startswith('foo')]
method2 = [x for x in strings if x[:3] == 'foo']

看上去它們的性能表現應該差不多,對吧?我們通過%time來確認一下:

In [561]: %time method1 = [x for x in strings if x.startswith('foo')]
CPU times: user 0.19 s, sys: 0.00 s, total: 0.19 s
Wall time: 0.19 s

In [562]: %time method2 = [x for x in strings if x[:3] == 'foo']
CPU times: user 0.09 s, sys: 0.00 s, total: 0.09 s
Wall time: 0.09 s

牆上時間(Wall time)是我們最感興趣的數字。所以,看上去第一個方法耗費了兩倍以上的時間,但這並不是一個非常精確的結果。如果你對相同語句多次執行%time的話,就會發現其結果是會變的。爲了得到更爲精確的結果,需要使用魔術函數%timeit。對於任意語句,它會自動多次執行以產生一個非常精確的平均執行時間。

In [563]: %timeit [x for x in strings if x.startswith('foo')]
10 loops, best of 3: 159 ms per loop
  
In [564]: %timeit [x for x in strings if x[:3] == 'foo']
10 loops, best of 3: 59.3 ms per loop

這個貌似平淡無奇的例子正好說明了一個事實:我們非常有必要了解Python標準庫、NumPy、pandas以及本書中所用到的其他庫的性能特點。在大型數據分析應用程序中,這些不起眼的毫秒數是會不斷累積的!

對於那些執行時間非常短(甚至是那些微秒(1e-6秒)或納秒(1e-9秒)級的)的分析語句和函數而言,%timeit是非常有用的。雖然這些時間值小到幾乎可以忽略不計,但同樣執行100萬次一個20微秒的函數,所用的時間要比一個5微秒的多15秒。在上面那個例子中,我們可以直接對那兩個字符串運算進行比較以瞭解其性能特點:

In [565]: x = 'foobar'

In [566]: y = 'foo'

In [567]: %timeit x.startswith(y)
1000000 loops, best of 3: 267 ns per loop

In [568]: %timeit x[:3] == y
10000000 loops, best of 3: 147 ns per loop 
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章