1. 程式人生 > >【翻譯】效能測量和技術提升

【翻譯】效能測量和技術提升

⚠️這個系列是自己瞎翻的,文法很醜,主要靠意會,跳著跳著撿重要的部分翻,翻錯了不負責,就這樣哈。

⚠️基於3.4.3,Performance Measurement and Improvement Techniques,原文

目標 

在處理影象的過程中,既然你每秒都要處理大量的資料計算,這就強制要求你的程式碼不僅僅要提供正確的解決邏輯,也要使用運算速度更快的方式。所以這一節,你會學到:

脫離開OpenCV,Python也提供了一個在測量執行時間時非常有用的模組 time 。另一個模組 profile 幫忙拿到程式碼的詳細報告,比如程式碼中每個方法要花掉多少時間,一個方法被呼叫了多少次等等。但是如果你使用IPython,所有的這些特性都被整合成了一種對使用者非常友好的方式。我們會遇到一些重要的特性,想要知道更多,檢視  額外資源 這個部分裡的連結。

用OpenCV來測量效能 

cv.getTickCount 方法返回一個特定的事件(比如開機的時刻)之後到你呼叫此方法的時刻之間的時間週期數。因此你在(自己的業務)方法執行前和(自己的業務)方法執行後呼叫這個方法,你就能得到之前執行(自己的業務)方法所花費的時間週期數。

cv.getTickFrequency 方法返回時間週期的頻率,或者叫做每秒時間週期的數量。所以要得到每秒執行的次數,你可以像下面這樣做。

e1 = cv.getTickCount()

# your code execution

e2 = cv.getTickCount()

time = (e2 - e1)/ cv.getTickFrequency()

我們將會用以下例子來論證,下面的示例用一個從5到49,奇數距離修正的種子,來做中值濾波。(不用管這個結果是啥樣的,那不是我們的目標):

img1 = cv.imread('messi5.jpg')
e1 = cv.getTickCount()
for i in xrange(5,49,2):
    img1 = cv.medianBlur(img1,i)
e2 = cv.getTickCount()
t = (e2 - e1)/cv.getTickFrequency()
print( t )
# Result I got is 0.521107655 seconds

提示

你可以用 time 模組來做同樣的事,用 time.time() 方法代替 cv.getTickCount ,然後拿到兩個時間的差值。

OpenCV裡的預設優化 

許多 OpenCV 方法用 SSE2, AVX 等等優化過。但OpenCV也包含了未優化的程式碼(譯者注:這對使用者是無感的,呼叫的方法相同,根據硬體和設定效率不同。)。所以如果你的系統支援這些特性我們就應該利用這些特性(幾乎所有的現代處理器都支援他們)。當編譯時它預設就開啟了,因此如果它被開啟了的話,OpenCV會執行優化過的程式碼,否則執行未優化過的。你可以使用 cv.useOptimized() 方法來檢查它是開啟了還是被關閉了,用 cv.setUseOptimized() 方法來開啟或者關閉它,讓我們看一個簡單示例。

# check if optimization is enabled
In [5]: cv.useOptimized()
Out[5]: True
In [6]: %timeit res = cv.medianBlur(img,49)
10 loops, best of 3: 34.9 ms per loop
# Disable it
In [7]: cv.setUseOptimized(False)
In [8]: cv.useOptimized()
Out[8]: False
In [9]: %timeit res = cv.medianBlur(img,49)
10 loops, best of 3: 64.1 ms per loop

看,優化過的中值濾波比未優化過的版本快兩倍。 如果你檢視它的原始碼,你可以發現這個中值濾波的計算是用單指令多資料流(譯者注:SIMD簡介)優化過的。所有你可以在你程式碼的最頂端用這個方式來開啟優化(記住它預設是開啟的)。

用IPython來測量效能 

有時候你可能需要比較兩個相似的方法的效能。IPython 給了你一個神奇的命令 timeit 來執行此操作。它執行程式碼幾次以獲得更準確的結果。 再特別說明一次,它只適用於測量單行程式碼。

例如,你知道以下哪個加法操作更好嗎,x = 5; y = x**2, x = 5; y = x*x, x = np.uint8([5]); y = x*x 還是 y = np.square(x) ?我們將用timeit在IPython 的指令碼中找到答案。

In [10]: x = 5
In [11]: %timeit y=x**2
10000000 loops, best of 3: 73 ns per loop
In [12]: %timeit y=x*x
10000000 loops, best of 3: 58.3 ns per loop
In [15]: z = np.uint8([5])
In [17]: %timeit y=z*z
1000000 loops, best of 3: 1.25 us per loop
In [19]: %timeit y=np.square(z)
1000000 loops, best of 3: 1.16 us per loop

你可以看到,x = 5 ; y = x*x 是最快的,並且比Numpy方法快了20倍左右(譯者注:結果中的單位-> ns-納秒10的-9次方秒,us-微秒10的-6次方秒)。如果你還考慮到陣列的建立,它可能要快到(Numpy的)接近100倍。很酷,對吧?*(Numpy 的開發團隊已經在著手解決這個小問題了)*

提示

Python標量操作比Numpy標量操作更快。 因此對於包含一個或兩個元素的操作,Python標量優於Numpy陣列。 當陣列的大小稍大時,Numpy會佔據優勢。

我們將再嘗試一個例子。 這次,我們將針對同一影象比較 cv.countNonZero() 和 np.count_nonzero() 的效能。

In [35]: %timeit z = cv.countNonZero(img)
100000 loops, best of 3: 15.8 us per loop
In [36]: %timeit z = np.count_nonzero(img)
1000 loops, best of 3: 370 us per loop

看,OpenCV方法比Numpy方法快了接近25倍。

提示

通常,OpenCV函式比Numpy函式更快。 因此,對於相同的操作,OpenCV方法是首選。 但是,可能有例外,尤其是當Numpy使用檢視而不是副本時。

IPython更多的神奇命令 

還有其他一些神奇的命令來衡量效能,分析,線性分析,記憶體測量等等。它們都有很好的文件記錄。因此,此處僅提供指向這些文件的連結。 建議有興趣的讀者試一試。(譯者注:在本節末尾的連線中)

效能優化技巧 

有幾種技術和編碼方法可以利用Python和Numpy最大化的效能。 此處僅註明相關的內容,並提供重要資源的連結。 這裡主要應該注意的是,首先嚐試以簡單的方式實現演算法。 一旦程式碼有效工作,分析它,找到瓶頸並進行優化它。

  1. 盡一切可能,避免在Python中使用迴圈,特別是雙/三迴圈。因為迴圈本身就很慢。
  2. 儘可能把演算法/程式碼向量化,因為Numpy和OpenCV都為向量操作做過優化。
  3. 利用快取的相關性。
  4. 除非必須,否則絕不拷貝陣列。嘗試用檢視代替,拷貝陣列是個消耗極大的操作。

即使照所有這些技巧做了之後,你的程式碼仍然很慢,或者使用大型迴圈是不可避免的,請使用其他庫比如 Cython 來加快速度。

額外資源 

Exercises


上篇:【翻譯】針對影象的演算法操作

下篇:【翻譯】改變色彩空間