怎樣測試程式的平均效能

programmer_lin發表於2013-12-25

標準庫中的sort函式,是快速排序演算法的典型實現。演算法將含有n個元素的序列排序,平均需要 O(n log n) 時間。

上週,我提出了“測試一個程式的效能比測試其功能更難”這個觀點。確認程式的效能達到標準以及確定“標準”的含義都十分困難。

接下來,我會繼續討論標準庫中的sort(排序)函式。sort函式實現了快速排序演算法,快速排序演算法平均可以在 O(n log n) 時間內對含有n個元素的序列進行排序。除了這個平均效能之外,如果選擇了“不幸”的輸入情況,快速排序的執行時間會比平均時間長很多,比如,某些情況下快速排序的時間複雜度可以達到O(n2)。我使用“不幸”這個詞是因為在快速排序的實現中經常使用隨機性的來保證O(n2)這樣的效能表現很少出現。

為什麼隨機性在這裡很重要呢?快速排序演算法開始時挑選序列中的一個特定元素開始排序,叫做pivot(中心資料)。然後,快速排序演算法調整元素的順序,使得小於等於中心資料的元素位於中心資料的前面,所有大於中心資料的元素排在中心資料的後面。最後,快速排序演算法遞迴呼叫,來完成對這兩部分元素的排序

因此,快速排序的執行時間,最壞情況下與元素個數對應的最大遞迴深度成比例。在實現中,依賴於遞迴深度的快速排序的效能,一般不會大於O(log n)。只要中心資料選定,遞迴深度就可以估計。平均情況下,這個深度與最大或最小元素的值無關。
快速排序演算法是怎樣確保選定的中心資料不是很接近序列的端點呢?一般來看,這是無法保證的。儘管如此,大部分情況下可以採用隨機選取中心資料的方法,來避免出現最壞情況。這樣做可以保證快速排序演算法的平均效能是可以接受的,即使在個別情況下,中心資料會出現在序列的端點位置,從而導致演算法效能低下。由於這樣的情況非常少見,對於平均效能來說它不是一個大問題。對嗎?

這要視情況而定。假設,你的任務是編寫效能測試程式,來測試快速排序的實現。

  • 你怎樣將c++標準中模糊的“平均效能”,改寫為實際需求,從而可以測試所有情況?
  • 你以怎樣的方式來測試快速排序,這樣的方式可以保證測試結果正確可靠?

測試平均效能之所以困難,是因為在這個概念中一個概率的因素。如果,程式最終必須產生一個特定結果,那麼,你可以確定一個測試程式的執行結果是正確還是錯誤。相反,如果你在測試平均效能,那麼對於一個單獨的測試用例,無法判斷執行結果是否正確。最好情況是,通過執行越來越多的測試用例,你可以更有把握程式是否正確執行。在這個測試的過程中,更多的測試可能會改變你對於程式正確性的判斷。

簡而言之,如果效能中包括了關於平均執行時間的描述,那麼相應的測試需要用到一些統計分析。這樣的分析並不簡單,但是這是工程應用的傳統。美國航空191號班機的空難就是一個例子。191號班機在1979年5月25日從奧黑爾國際機場起飛。當飛機剛剛離開地面時,飛機左翼引擎忽然失靈並且從機翼上脫落。引擎是通過安全銷連線在機翼上的,這樣的設計是為了與機翼脫離而不是毀壞機翼。儘管如此,由於維護失誤,機翼被毀,導致飛機失控,發生空難,機上所有人無一倖免。

在閱讀相關的調查中,我看見了不同的飛機制造商對安全銷進行的測試,證明— 假設飛機正常維護 — 安全銷會使得引擎離開機翼而不是毀壞機翼。在此之前,我沒有見到過這樣的測試,但是設計安全銷在工程商的主要問題是:安全銷的目的是,受到過大壓力時,使引擎和機翼脫落。沒有辦法在安全銷不被破壞的情況下測試安全銷是否滿足要求。因此,在飛機中實際使用的安全銷不能檢測。

人們怎樣才能確保使用這樣生產的安全銷不會影響飛機的安全性呢?答案設計非常聰明。

  • 引擎被許多個安全銷固定在機翼上,這樣即使有一個安全銷失靈,引擎也能從機翼上分離,而不損壞機翼。
  • 安全銷以100個為一批進行生產,一個批次內的安全銷同時以同樣的方式生產製造。
  • 每個批次的100個安全銷中,會被隨機挑選10個進行測試,因此,10個安全銷會在測試中毀壞。如果,10個安全銷都通過了測試,那麼就認為剩下的90個安全銷是安全可用的。如果10箇中有一個安全銷沒有通過測試,那麼這個批次都會被銷燬。

顯然,這樣的設計不僅僅包括了巧妙的工業設計,也包含了精妙的統計分析。必須準確選擇對安全銷的限制條件,在10%的安全銷已經通過測試的條件下,即使兩個隨機選取的安全銷也幾乎不可能超出限制條件。我推測,這樣的限制條件可能是比實際應用中安全銷需要滿足的限制條件更窄的範圍。

我不想為了評估快速排序演算法的平均效能而進行這樣的統計分析。即使我有信心可以正確地進行類似的統計分析,將來可能出現的規範或者測試程式的改變,都會使這樣的分析無效。同時,在以快速排序為例的演算法,和以安全銷為例的機械裝置之間有一個重要區別,那就是,有時為了達到某些目的,演算法的輸入會被設計的非常複雜。比如在Doug McIlroy 1999年寫的論文中,詳細描述了怎樣構造快速排序的輸入,使得演算法對於n個元素的排序時間達到O(n2)。在這樣的情況下,快速演算法就與描述不符了嗎?如果是這樣,那麼就很難看見我們現在對快速演算法的應用了。

使得這樣的效能測試問題簡單化的一個方法是採用白盒測試的方法。白盒測試的方法利用了已知程式實現細節這一優勢。下週,我會詳細介紹這樣的測試技術。

相關文章