此文完整連結 http://note.tc.edu.tw/264.html
這是民國97年10月16日左右寫的程式,今天把他整理一下。先丟出一個問題:
Q. 在同一台機器上。假設一個單執行緒的 A 程式,例如產生200萬組的亂數,用本機在Console下執行要花 Ta秒。而同樣的功能寫一個B,差別在於B 會開啟一個TCP的埠,接受程式C傳入的參數,進行運算,運算完畢再將結果丟回給C,而C不負責運算,只負責接線到B,將參數告訴B,再接收B算完的結果,同樣也算200萬組的亂數,用本機在Console下執行要花 Tb秒,請問 Ta大還是 Tb大?
思考.
Ta = 程式A執行產生及分類200萬組變數的時間;
Tb = 程式C和B建立連線時間 + 程式B 產生及分類200萬組變數執行的時間 + 程式B 將結果回傳給C 的時間 。
由於產生及分類200萬組變數的程式碼都相同,也在同一台機器上執行,所以合理的推論
程式A執行產生及分類200萬組變數的時間 =近似於= 程式B 產生及分類200萬組變數執行的時間,而Tb多了TCP連線及傳參、傳結果的時間,所以Tb理論上應微大於 Ta,所以 Tb > Ta
實驗結果,出人意料, Ta > Tb 本機執行的A程式花費時間,竟然比同台機器上的網路程式B來得多,而且多很多。
說明
1. 程式 A 和 B 的功能都是產生一個常態分配的亂數,有關細節可以參考這篇;現在都設定為產生200萬組,標準差為1,然後以1為間距分類,例如6代表5.5<=x<6.5的亂數; 以下是分類的結果:
$ ./a.pl <==
Program Start....
1 61
2 4493
3 119461
4 1212828
5 4833075
6 7658486
7 4835924
8 1211702
9 119407
10 4504
11 58
12 1
Elapsed time=00:01:34.584939
測試第二次,時間也落在00:01:34左右,差距不大,經實測10多次,差距非常小。
1 52
2 4453
3 120136
4 1211275
5 4835101
6 7656908
7 4836712
8 1210831
9 119857
10 4610
11 64
12 1
Elapsed time=00:01:34.379488
平均時間 1分34秒 = 94秒
2. 程式B為一個RPC 的Server,負責接收來自 RPC 的 Client (程式C)傳進來的參數,例如組數200萬筆,均值6,標準差為1,然後以1為間距分類等參數。運算完畢再將結果回傳給程式C
1 78
2 4548
3 118873
4 1210720
5 4830204
6 7660240
7 4836865
8 1213987
9 119893
10 4513
11 79
Elapsed time=00:01:24.393868
測試第二次,時間也落在00:01:24左右,差距不大,經實測10多次,差距非常小。
1 65
2 4506
3 119423
4 1210867
5 4836787
6 7658475
7 4833891
8 1211412
9 119771
10 4731
11 71
12 1
Elapsed time=00:01:24.345658
平均時間 1分24秒 = 84秒
3. 程式C只是一個 RPC 的 Client ,只負責傳送參數給 RPC Server 及接收計算完的結果,本身不負責計算。
4. 三個程式都是單一執行緒,計算時間檢視 CPU的Loading,只有一顆在>90%,其他都低於3%,而C程式執行時,另一顆CPU會有一瞬間(少於1s)達5%~7%。
5. 本機程式 B花費時間是 A 的 84/94 = 89%,表示效能約高 11%。無法解釋這個現象,只能把結果寫出來,而程式碼附於後。
[附錄一] 測試機器
以下兩台為測試過的機器,兩台的結果Tb < Ta是相同的
1. CentOs4.3 Linux 2.6.9-34.ELsmp Xeon3.0 x2
2. Ubuntu Linux 8.04 2.6.11 HP IA-64 位元主機
[附錄二] 程式碼,這裡就不解釋程式碼了,請自行參看
A 程式
其中的副函式:
#create Normal Distribution random value sub dev{ my %h=(); local $meanvalue = shift; local $randnum = shift; local $sd = shift; for(1..$randnum){ do{ $v1 = (2 * rand 1) - 1; $v2 = (2 * rand 1) - 1; $r = $v1 **2 + $v2 **2; }while($r >= 1); $fac = sqrt(-2 * log($r)/ log(exp(1)) / $r); $gauss = $v2 * $fac; $vr= $gauss * $sd + $meanvalue; $sddif = $sd*$dif; $hid= int( ( $vr + $sddif /2 ) / $sddif ) * $sddif; $h{$hid}++; } # end for return %h; } # end dev |
B 程式
B 程式本身是 RPC的 Server,所以我去改他的 RPC 套件 RPC::Simple:
#!/usr/bin/perl -w package main ; use RPC::Simple::Server; use RPC::Simple::Factory; my $arg = shift ; my $verbose = 1 ; # you may change this value to see RPC traffic my $pid = &spawn( undef ,$verbose) ; # spawn server sleep(3600); # 執行後放在記憶體中一個小時,這行是我花了很多時間才想到應該要加的。 |
這個 B 程式會產生 RPC Server,他預設會去包括起一個 library 檔 MyRemote.pm
lib/perl5/site_perl/5.8.8/RPC/Simple/AnyLocal.pm:#@_ [MyLocal=HASH(0x82fb0fc) RPC::Simple::Factory=HASH(0x812fb44) MyRemote.pm]
lib/perl5/site_perl/5.8.8/RPC/Simple.pm: $self->createRemote($remote,'MyRemote.pm') ;
此檔為 Server端的函式檔:
package MyRemote ; # 接收參數並呼叫函式 dev 進行運算 |
#計算常態分佈標準差並分組的函式這裡和程式A的 dev 函式完全相同,不再列出
#create Normal Distribution random value
sub dev{
...程式A的 dev 函式完全相同,不再列出
}
C 程式
C 程式本身是 RPC的 Client
#!/usr/bin/perl -w use RPC::Simple::Factory ; use Time::Elapse; use IO::Socket ; use IO::Select ; print "\nProgram Start....\n"; Time::Elapse->lapse(my $now); my $verbose = 1 ; # you may change this value to see RPC traffic my $factory = new RPC::Simple::Factory(verbose_ref => \$verbose, remote_host=> '163.17.38.83') ; # print $factory."\n"; my $local = new MyLocal($factory) ; # $local->remoteAsk(callback => 'answer'); # 叫用RPC Server 的函式 NormalDistribution,注意本地端並不存在此函式 $local->NormalDistribution(callback => 'answer', randnum=> 20000000, meanvalue=>6, sd=>1, dif=>1); #等待回傳的結果 #sleep(3); my $selector = IO::Select->new(); $selector->add($factory->getSocket()); # sleep(0.01); my ($toRead, undef, undef) = IO::Select->select($selector, undef, $selector, 10); foreach my $fh (@$toRead) { if($fh == $factory->getSocket()) { $factory->readSock(); } } print "Elapsed time=". $now. "\n"; |