使用Student T'test方法做性能測試

性能測試

日常工作中對比函數間的快慢時,最直接的方法就是根據timer:tc/1結果的時間來衡量,比如想知道lists:reverse/1與直接使用自己寫的尾遞歸函數誰更快?最直接的方法就是

-module(test).
-export([start/1]).
start(Len) ->
    L = lists:seq(1, Len),
    Max = 1000,
    Time1 = benchmark(Max, fun() -> lists:reverse(L) end),
    Time2 = benchmark(Max, fun() ->  tail_reverse(L) end),
    Time1/Time2.

benchmark(Max, Fun) -> benchmark(Max, Fun, 0).

benchmark(0, _Fun, Time) -> Time;
benchmark(Count, Fun, Acc) ->
    {Time, _} = timer:tc(Fun),
    benchmark(Count - 1, Fun, Time + Acc).

tail_reverse(L) -> tail_reverse(L, []).
tail_reverse([], Acc) -> Acc;
tail_reverse([X | Xs], Acc) -> tail_reverse(Xs, [X | Acc]).

在shell中直接運行

1> [begin test:start(Len) end||Len<-lists:seq(1,10000, 1000)].
[0.9605911330049262,0.9538599640933573,0.4008409250175193,
 0.4468459717078502,0.42090136752676627,0.5073483137748491,
 0.4653574844571975,0.47523496362271184,0.508740383302226,
 0.5094403169886083]

可以看出,lists:reverse/1比尾遞歸快,且列表長度越大時,lists:reverse/1越優。
但是以上的結論建立在所有的測試條件完全一致的情況下。但達到完全一致是完全不可能的,因爲在Erlang VM在測試過程中也可能在做其它的事(GC,其它調度工作). 即便再優化代碼,儘量保持VM不在幹其它的事,但是還需要保證測試過程中操作系統條件一致,假如測試時使用的是筆記本電腦測試,你可能會在測試的過程中不經意的切換了一下web瀏覽器,看一下視頻,郵件,或者由於電量問題系統自動切換了CPU的頻次。
換句話來說,有太多擾亂性能測試結果的因素,其中有一些我們無法察覺或無法控制的,但它們可能不會發生或可能不重要,這些變數混雜在一起讓本來需要精確對比的結果變得難以衡量。

所以這種求平均數的方法在需要精確對比的性能測試中並沒有十足的說服力。Student't T-Test就是專用於解決這類問題的。

Student's T-Test

T-Test通過對比平均數(averages)和調和平均數(means),可以告訴你樣本間不同的程度有多大。它可以讓你知道這些差異是否是偶然發生的。舉個例子:
一家制藥公司可能想要測試一種新的抗癌藥物,以確定它是否可以提高預期壽命。在一項實驗中,總有一個對照組(給予安慰劑的組)。對照組可能顯示平均預期壽命爲+5年,而服用新葯的組可能預期壽命爲+6歲。似乎該藥物可能起作用。但這可能是由於僥倖。爲了測試這一點,研究人員將使用T-Test檢驗來確定整個人羣的結果是否可重複。
T-Test看起來幾乎與正態分佈曲線相同,只是更短更胖。當您有小樣本時,使用t分佈而不是正態分佈。樣本量越大,t分佈越接近正態分佈。實際上,對於大於20的樣本大小(例如:更大的自由度),分佈幾乎與正態分佈完全相同。
如何一步一步手動計算配對T-Test

  1. 假如有以下兩個樣本。

  2. 使用X-Y得到差值。

  3. 把上一步得到的所有差值都相加。

  4. 計算第一步中X-Y的平方值。

  5. 把第三步中的所有平方值相加。

  6. 使用公式計算t-score。

ΣD: Sum of the differences (Sum of X-Y from Step 2)
ΣD2: Sum of the squared differences (from Step 4)
(ΣD)2: Sum of the differences (from Step 2), squared.

  1. 樣本的個數-1得到自由度(the degrees of freedom) 11 -1 = 10。
  2. 通過查t-table表得到p-value。使用第6步得到的自由度查表。我們使用95%的Confidence level, 所以對應的此樣本是 df=10 alpha level = 5% 查得t-value爲2.228。

  3. 對比2.228和在第5步計算的-2.74, -2.74的絕對值大於2.228所以 p-value < 0.05。我們可以拒絕無效假設,即均值之間沒有區別。

eminstat的使用及結果分析

那麼如何把T-Test運用到我們的性能測試中呢?
eminstat就是使用T-Test方法來測試檢驗樣本間是否有差異的庫。它使用非常簡單,不過要完全看懂它的結果,必須要對T-Test有一定的瞭解。
下面我們還是以reverse的例子來詳細介紹一下eminstat使用和結果分析。

rebar3新建庫並更新依賴。

rebar3 new lib bencherl
===> Writing bencherl/src/bencherl.erl
===> Writing bencherl/src/bencherl.app.src
===> Writing bencherl/rebar.config
===> Writing bencherl/.gitignore
===> Writing bencherl/LICENSE
===> Writing bencherl/README.md

cat rebar.config
{deps, [{eminstat, {git, "https://github.com/jlouis/eministat.git", {branch, master}}}]}.

rebar3 compile

編寫測試用例

cat ebench_reverse.erl
-module(ebench_reverse).

-export([t/0, t/2]).

-define(LOOP, 1000).

t() ->
    t(1000, 40).

t(Len, Count) ->
    [H | T] = datasets(Len, Count),
    eministat:x(95.0, H, T).

lists_r(Items) -> lists_r(Items, ?LOOP).

lists_r(_, 0) -> ok;
lists_r(Items, K) ->
    lists:reverse(Items),
    lists_r(Items, K-1).

tail_r(Items) -> tail_r(Items, ?LOOP).

tail_r(_, 0) -> ok;
tail_r(Items, K) ->
    tail_reverse(Items),
    tail_r(Items, K-1).

datasets(Len, Count) ->
    Items = lists:seq(1, Len),
    [
        eministat:s("lists:reverse", fun() -> lists_r(Items) end, Count),
        eministat:s("tail_reverse", fun() -> tail_r(Items) end, Count)
    ].

tail_reverse(L) -> tail_reverse(L, []).
tail_reverse([], Acc) -> Acc;
tail_reverse([X | Xs], Acc) -> tail_reverse(Xs, [X | Acc]).

得到測試結果

rebar3 shell
1>ebench_reverse:t(3000, 30).
x lists:reverse
+ tail_reverse
+--------------------------------------------------------------------------+
|xxxxxxx x                                    ++++                        +|
|xxxxx x                                      ++++                         |
|xxxx                                         +++                          |
|xxx                                          +++                          |
|xx                                           +++                          |
| x                                           +++                          |
| x                                            +                           |
| x                                            +                           |
| x                                            +                           |
| x                                            +                           |
| x                                            +                           |
| x                                            +                           |
|                                              +                           |
|                                              +                           |
|                                              +                           |
||MA_|                                                                     |
|                                          |___MA____|                     |
+--------------------------------------------------------------------------+
Dataset: x N=30 CI=95.0000
Statistic     Value     [         Bias] (Bootstrapped LB‥UB)
Min:            8295.00
1st Qu.         8494.00
Median:         8700.00
3rd Qu.         9467.00
Max:         1.14400e+4
Average:        9088.53 [     -3.07012] (      8849.00 ‥       9447.13)
Std. Dev:       831.129 [     -28.0026] (      608.188 ‥       1129.95)

Outliers: 0/1 = 1 (μ=9085.46, σ=803.126)
        Outlier variance:      0.484430 (moderate)

------

Dataset: + N=30 CI=95.0000
Statistic     Value     [         Bias] (Bootstrapped LB‥UB)
Min:         2.67010e+4
1st Qu.      2.70370e+4
Median:      2.71440e+4
3rd Qu.      2.75240e+4
Max:         3.82770e+4
Average:     2.76068e+4 [     -3.36850] (   2.72001e+4 ‥    2.91141e+4)
Std. Dev:       2043.00 [     -372.223] (      307.987 ‥       4224.71)

Outliers: 0/1 = 1 (μ=2.76034e+4, σ=1670.78)
        Outlier variance:      0.384784 (moderate)

Difference at 95.0% confidence
        1.85183e+4 ± 806.172
        203.754% ± 8.87022%
        (Student's t, pooled s = 1559.59)
------

ok

我們對長度爲3000的List做reverse,取樣30次。得到上面的結果。
我們的數據必須是正態分佈,2組樣本的方差要一致。如果不一致,測試結果就無效,你必須找其它更厲害的工具(比如:R Tool)來分析。

測試方法

首先收集樣本
DataSet = eminstat:s(Name, Function, N) : 運行Function N次,並收集每一次的運行時間做爲DataSet。然後使用:
eninistat:x(ConfidenceLevel, DataSet1, [DataSet,...])來分析樣本。
ConfidenceLevel可以爲[80.0,90.0,95.0,98.0,99.0,99.5] 一般使用95%就夠了。用於查表得到t-value。

結果分析

結果包括4個部分:

圖表(histogram)
對每一組樣本
           要素(vitals)
           孤立點分析(outlier analysis)
T-Test結果

圖表(Histogram)

x lists:reverse
+ tail_reverse
+--------------------------------------------------------------------------+
|xxxxxxx x                                    ++++                        +|
|xxxxx x                                      ++++                         |
|xxxx                                         +++                          |
|xxx                                          +++                          |
|xx                                           +++                          |
| x                                           +++                          |
| x                                            +                           |
| x                                            +                           |
| x                                            +                           |
| x                                            +                           |
| x                                            +                           |
| x                                            +                           |
|                                              +                           |
|                                              +                           |
|                                              +                           |
||MA_|                                                                     |
|                                          |___MA____|                     |
+--------------------------------------------------------------------------+

把樣本的所有的數據都使用ASCII art的點到圖表上,可以看到數據總體分佈。如果數據有重疊的,就使用*來代替。
M代表median中位數,
A代表Average/Mean平均數。
如果只顯示一個A,就說明MA重疊了。
|____…____|代表假定爲正態分佈的數據集時表示1個標準偏差。越窄越穩定。

要素(Vitals)

Dataset: x N=30 CI=95.0000
Statistic     Value     [         Bias] (Bootstrapped LB‥UB)
Min:            8295.00
1st Qu.         8494.00
Median:         8700.00
3rd Qu.         9467.00
Max:         1.14400e+4
Average:        9088.53 [     -3.07012] (      8849.00 ‥       9447.13)
Std. Dev:       831.129 [     -28.0026] (      608.188 ‥       1129.95)

N 樣本的個數。
CI 上面說的信任等級[80.0,90.0,95.0,98.0,99.0,99.5]
Min 最小值
Max 最大值
Median 中位數
1st Qu 1/4的中位數位置(就是min與Median之前的中位數)
3st Qu 3/4的中位數位置(就是Median與Max之前的中位數)
Average 計算數據集的平均值以及標準偏差
爲了比單點估計更精確,還提供了具有下限和上限的平均值和標準差的間隔。也就是說,eministat可以從這個特定樣本獲得的平均值是8849 ‥ 9447,而不是告訴平均值是9088作爲一個點。
置信等級爲95%並不意味着真實的總體平均值的可能性爲95%,沒有任何程序可以做到這點,它代表:
如果你要重複這個實驗數千次,並且你會設置每個樣本的(不同的)置信區間,那麼它有95%的概率是會包含真實的平均值。
系統使用偏差校正的加速引導方法(bias-corrected accelerated bootstrap method)來計算間隔的邊界。它還計算從引導程序到樣本參數的偏差。在上面的例子中,你會在Std中看到-28的偏差。這個偏差意味着一般引導程序(bootstrap)獲得比樣本估計值更小的標準偏差。

孤立點分析(Outliers)

Outliers: 0/1 = 1 (μ=9085.46, σ=803.126)
        Outlier variance:      0.484430 (moderate)

emninstat使用非常簡單計算孤立點的方法

IQR = Q3 - Q1

所以離中位數有1.5IQR的都會被認爲是孤立點。
Lower/Upper 代表着較低的異常值低於平均值的個數/較高的異常值高於平均值的個數。
Outlier variance 孤立點的方差,代表着孤立點對最終結果的影響程度,等級爲
unaffected‥slight‥moderate‥severe 如果結果爲severe,你就應該想辦法優化一下樣本,來避免這種影響。
影響孤立點的因素有很多:垃圾回收,CPU頻次變化,其它系統的後臺任務。

T-Test

Difference at 95.0% confidence
        1.85183e+4 ± 806.172
        203.754% ± 8.87022%
        (Student's t, pooled s = 1559.59)

上面代表着:

  1. 以上測試數據集符合正態分佈,或近似於正態分佈。
  2. 以上測試數據集有一樣的方差。
    pooled :彙總(組合)方差,https://en.wikipedia.org/wiki/Pooled_variance
    1.85183e+4 ± 806.172: 平均值(means) ± 間隔(interval)。
    203.754% ± 8.87022%
    203.754 = 1.85183e+4/9088.53(means) * 100
    8.87022 = 806.172/9088.53(means) * 100
    means是爲第一個樣本的means。

所以上面的結果可以確切得出: lists:reverse尾遞歸快得多,且穩定。

參考鏈接

http://www.statisticshowto.com/probability-and-statistics/t-test/
https://en.wikipedia.org/wiki/Pooled_variance
https://github.com/jlouis/eministat

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章