从接触Golang开始,断断续续已有差不多一年左右的时间了,都是业余自己学学看看,尚主要限于语法及语言特性,还没有用它写过实际的项目。
关于Golang的语法及语言特性,网上有很多资源可以学习。后面某个时间,我也许会写一篇粗浅的文章,来比较一下Golang和C++、Delphi甚至C#等语言语法方面的特性。
我算是个急性子的人(当然现在好一些了),于是作为码农,显而易见会对“效率”比较敏感。这里的效率不单单指编译器生成的机器码优化程度,也包括编译器的编译速度,所以我对C++兴趣不算大,虽然它是我平时的工作语言。
言归正传。
分别用Golang、C++、Delphi写了四个小例子,包括普通的应用场景、字符串(串接)操作及数据密集计算(当然也会涉及到譬如库函数的优化等)。我的电脑软硬件环境为:Win764bit,XeonE3-1230(8核),16GRAM。Golang版本是1.3.1Windows/386,VC则用的VS2012,而Delphi则用的XE6Update1。VC和Delphi编译设置为Win32&Release,Golang则使用默认配置。
所有测试计量单位均为毫秒(ms)。
首先是计算π的例子,代码分别如下。
Golang:
packagemain import( "fmt" "time" ) constcNumMax=999999999 funcmain(){ sign:=1.0 pi:=0.0 t1:=time.Now() fori:=1;i<cNumMax+2;i+=2{ pi+=(1.0/float64(i))*sign sign=-sign } pi*=4 t2:=time.Now() fmt.Printf("PI=%f;Time=%d\n",pi,t2.Sub(t1)/time.Millisecond) } C++: #include"stdafx.h" #include<stdio.h> #include<time.h> int_tmain(intargc,_TCHAR*argv[]) { constintcNumMax=999999999; doublesign=1.0; doublepi=0; clock_tt1=clock(); for(inti=1;i<cNumMax+2;i+=2) { pi+=(1.0f/(double)i)*sign; sign=-sign; } pi*=4; clock_tt2=clock(); printf("PI=%lf;Time=%d\n",pi,t2-t1); return0; }Delphi:
programPiCalcer; {$APPTYPECONSOLE} {$R*.res} uses System.SysUtils,System.DateUtils; const cNumMax=999999999; var Sign:Double=1.0; Pi:Double=0.0; I:Integer; T1:Double; T2:Double; S:string; begin T1:=Now; I:=1; whileI<cNumMax+2do begin Pi:=Pi+(1.0/I)*Sign; Sign:=-Sign; I:=I+2; end; Pi:=Pi*4; T2:=Now; S:=Format(PI=%.6f;Time=%d,[Pi,MilliSecondsBetween(T2,T1)]); Writeln(S); Readln; end.分别执行10次,结果如下。
Golang:2038202820362024203420152034201820242018,平均:2026.9;
C++:2041205220622036203320492039202620372038,平均:2041.3;
Delphi:2594257225742584257425642575257525712563,平均:2574.6。
结果居然很不错,比VC还快,而Delphi,大家都懂,优化向来不是它的“强项”。
然后是个质数生成例子。
Golang:
packagemain import( "fmt" "time" ) constcNumMax=10000000 funcmain(){ t1:=time.Now() varnums[cNumMax+1]int vari,jint fori=2;i<cNumMax+1;i++{ nums[i]=i } fori=2;i<cNumMax+1;i++{ j=2 forj*i<cNumMax+1{ nums[j*i]=0 j++ } } cnt:=0 fori=2;i<cNumMax+1;i++{ ifnums[i]!=0{ cnt++ } } t2:=time.Now() fmt.Println("Time:",t2.Sub(t1),"Count:",cnt) } C++: #include"stdafx.h" #include<stdlib.h> #include<time.h> constintcNumMax=10000000; int_tmain(intargc,_TCHAR*argv[]) { clock_tt1=clock(); int*nums=(int*)malloc(sizeof(int)*(cNumMax+1)); inti; for(i=2;i<cNumMax+1;i++) { nums[i]=i; } intj; for(i=2;i<cNumMax+1;i++) { j=2; while(j*i<cNumMax+1) { nums[j*i]=0; j++; } } intcnt=0; for(i=2;i<cNumMax+1;i++) { if(nums[i]!=0) { cnt++; } } free(nums); clock_tt2=clock(); printf("Time:%dms;Count:%d\n",t2-t1,cnt); }Delphi:
programPrimeSieve; {$APPTYPECONSOLE} {$R*.res} uses System.SysUtils,System.DateUtils; const cNumMax=10000000; var T1,T2:Double; I,J:Integer; Cnt:Integer; Nums:arrayofInteger; begin T1:=Now; SetLength(Nums,cNumMax+1); forI:=2tocNumMaxdo Nums[I]:=I; forI:=2tocNumMaxdo begin J:=2; whileJ*I<cNumMax+1do begin Nums[J*I]:=0; Inc(J); end; end; Cnt:=0; forI:=2tocNumMaxdo begin ifNums[I]<>0then Inc(Cnt); end; SetLength(Nums,0); T2:=Now; Writeln(Format(Cnt=%d;Time=%d,[Cnt,MilliSecondsBetween(T2,T1)])); Readln; end.同样分别执行10次,结果如下。
Golang:959957959953961951948956956956,平均:955.6;
C++:965965967953961964963960956956,平均:961;
Delphi:973976973982981970977979971977,平均:975.9;
仍然,Golang看上去最快,而Delphi则很正常地居末。
所以我忍不住想要来一个能展现Delphi优点的例子,这个例子几乎毫无疑问,和字符串操作(及内存管理器)相关,所以有如下字符串串接的示例(其中涉及到了譬如IntToStr/itoa这样的函数调用,我自己实现了个C++版的IntToStr)。
Golang:
packagemain import( "bytes" "fmt" "strconv" "time" ) constcNumMax=1000000 //bytes.Buffer(7.2.6) functestViaBuffer()string{ varbufbytes.Buffer fori:=0;i<cNumMax;i++{ buf.WriteString(strconv.Itoa(i)) } returnbuf.String() } //+= functestViaNormal()string{ varretstring fori:=0;i<cNumMax;i++{ ret+=strconv.Itoa(i) } returnret } funcmain(){ fmt.Println("Testviabytes.Buffer...") t1:=time.Now() s:=testViaBuffer() t2:=time.Now() fmt.Printf("Result:%s...(Length=%d);Time:%dms\n",s[2000:2005],len(s),t2.Sub(t1)/time.Millisecond) /* fmt.Println("Testvianormalway...") t1=time.Now() s=testViaNormal() t2=time.Now() fmt.Printf("Result:%s...(Length=%d);Time:%dms\n",s[2000:2005],len(s),t2.Sub(t1)/time.Millisecond) */ } C++: #include"stdafx.h" #include<time.h> #include<stdarg.h> #include<string> #include<iostream> usingnamespacestd; constintcNumMax=1000000; wstringFormatV(constwchar_t*pwcFormat,va_listargList) { wstringws; intnLen=_vscwprintf(pwcFormat,argList); if(nLen>0) { ws.resize(nLen); vswprintf_s(&ws[0],nLen+1,pwcFormat,argList); } returnws; } wstring__cdeclFormat(constwchar_t*pwcFormat,...) { va_listargList; va_start(argList,pwcFormat); wstringws=FormatV(pwcFormat,argList); va_end(argList); returnws; } stringFormatVA(constchar*pcFormat,va_listargList) { strings; intnLen=_vscprintf(pcFormat,argList); if(nLen>0) { s.resize(nLen); vsprintf_s(&s[0],nLen+1,pcFormat,argList); } returns; } string__cdeclFormatA(constchar*pcFormat,...) { va_listargList; va_start(argList,pcFormat); strings=FormatVA(pcFormat,argList); va_end(argList); returns; } wstringIntToStr(intnValue) { returnFormat(L"%d",nValue); } stringIntToStrA(intnValue) { returnFormatA("%d",nValue); } wstringtestW() { wstringret=L""; for(inti=0;i<cNumMax;i++) { ret+=IntToStr(i); } returnret; } stringtest() { stringret=""; for(inti=0;i<cNumMax;i++) { ret+=IntToStrA(i); } returnret; } int_tmain(intargc,_TCHAR*argv[]) { cout<<"Startingtestwithaloopnumof"<<cNumMax<<endl; clock_tt1=clock(); strings=test(); clock_tt2=clock(); cout<<"Result:"<<s.substr(2000,5)<<"..."<<";Size:"<<s.size()<<";Time:"<<t2-t1<<"ms"<<endl; cout<<endl; cout<<"StartingtestforWSTRINGwithaloopnumof"<<cNumMax<<endl; t1=clock(); wstringws=testW(); t2=clock(); wcout<<"Result:"<<ws.substr(2000,5)<<"..."<<";Size:"<<ws.size()<<";Time:"<<t2-t1<<"ms"<<endl; return0; }Delphi:
programStrPerformanceTest; {$APPTYPECONSOLE} {$R*.res} uses System.SysUtils,System.DateUtils; const cNumMax=1000000; functionTestViaStringBuilder:string; var SB:TStringBuilder; I:Integer; begin SB:=TStringBuilder.Create; forI:=0tocNumMax-1do SB.Append(IntToStr(I)); Result:=SB.ToString; FreeAndNil(SB); end; functionTestViaNormal:string; var I:Integer; begin Result:=; forI:=0tocNumMax-1do Result:=Result+IntToStr(I); end; var T1:Double; T2:Double; S:string; begin Writeln(Startingtestwithaloopnumof,cNumMax,...); T1:=Now; S:=TestViaStringBuilder; T2:=Now; Writeln(Format(TestviaTStringBuilderresult:%s...(Length=%d);Time:%dms,[Copy(S,2001,5),Length(S),MilliSecondsBetween(T2,T1)])); T1:=Now; S:=TestViaNormal; T2:=Now; Writeln(Format(Testvianormal-way(+=)result:%s...(Length=%d);Time:%dms,[Copy(S,2001,5),Length(S),MilliSecondsBetween(T2,T1)])); Readln; end.分别执行10次。悲剧的是,Golang里的字符串+=操作实在太慢了,我实在不想等下去,所以只给出了其官方推荐的使用bytes.Buffer的结果。而在这个例子中,Delphi使用TStringBuilder并未显示出什么优化(FastMM实在太强悍了!),所以我也只给出了普通的串接结果(AnsiString和string都是Delphi的原生类型,有着类同的内存布局,效率上应没有什么差别,所以这里只测试了string)。
Golang:141148134119133123145127122132,平均:132.4;
C++(std::string):384400384385389391389384390383,平均:387.9;
C++(std::wstring):519521522521519522518519518518,平均:519.7;
Delphi(string):41414141414141414441,平均:41.3;
果然,Delphi大幅领先,当然这主要归功于FastMM,这个开源的Pascal家族的内存管理器实在太强大了!
当然这个测试对C++并不公平,因为Golang的写法并非普通的串接,只是我不知道STL或Boost里有无类似StringBuilder这样的利器呢?
最后是个数据密集计算型的例子。
Golang:
packagemain import( "fmt" "time" ) constcSizeint=30 typemymatrix[cSize][cSize]int funcmkmatrix(rows,colsint,mx*mymatrix){ rows-- cols-- count:=1 forr:=0;r<=rows;r++{ forc:=0;c<=cols;c++{ mx[r][c]=count count++ } } } funcmultmatrix(rows,colsint,m1,m2*mymatrix,mm*mymatrix){ rows-- cols-- fori:=0;i<=rows;i++{ forj:=0;j<=cols;j++{ val:=0 fork:=0;k<=cols;k++{ val+=m1[i][k]*m2[k][j] mm[i][j]=val } } } } funcmain(){ varm1,m2,mmmymatrix mkmatrix(cSize,cSize,&m1) mkmatrix(cSize,cSize,&m2) t0:=time.Now() fori:=0;i<=100000;i++{ multmatrix(cSize,cSize,&m1,&m2,&mm) } t:=time.Since(t0) fmt.Println(mm[0][0],mm[2][3],mm[3][2],mm[4][4],mm[29][29]) fmt.Println("tick=",t) } C++: #include"stdafx.h" #include<time.h> #include<iostream> usingnamespacestd; constintMATRIX_SIZE=30; intMatrix[MATRIX_SIZE][MATRIX_SIZE]; voidMakeMatrix(introws,intcols,intmx[MATRIX_SIZE][MATRIX_SIZE]) { rows--; cols--; intcount=1; for(intr=0;r<=rows;r++) { for(intc=0;c<=cols;c++) { mx[r][c]=count; count++; } } } voidMatrixMult(introws,intcols,constintm1[MATRIX_SIZE][MATRIX_SIZE],constintm2[MATRIX_SIZE][MATRIX_SIZE],intmx[MATRIX_SIZE][MATRIX_SIZE]) { rows--; cols--; intval; for(inti=0;i<=rows;i++) { for(intj=0;j<=cols;j++) { val=0; for(intk=0;k<=cols;k++) { val+=m1[i][k]*m2[k][j]; mx[i][j]=val; } } } } int_tmain(intargc,_TCHAR*argv[]) { intnum=100000; intm1[MATRIX_SIZE][MATRIX_SIZE],m2[MATRIX_SIZE][MATRIX_SIZE],mx[MATRIX_SIZE][MATRIX_SIZE]; MakeMatrix(MATRIX_SIZE,MATRIX_SIZE,m1); MakeMatrix(MATRIX_SIZE,MATRIX_SIZE,m2); clock_tt1=clock(); for(inti=0;i<=num;i++) { MatrixMult(MATRIX_SIZE,MATRIX_SIZE,m1,m2,mx); } clock_tt2=clock(); cout<<mx[0][0]<<""<<mx[2][3]<<""<<mx[3][2]<<""<<mx[4][4]<<endl; cout<<t2-t1<<"ms"<<endl; return0; }Delphi:
programProject1; {$APPTYPECONSOLE} {$R*.res} uses System.SysUtils,System.DateUtils; const cSize=30; type TMatrix=array[0..cSize-1,0..cSize-1]ofInteger; procedureMakeMatrix(Rows,Cols:Integer;varMx:TMatrix); var R,C,Count:Integer; begin Dec(Rows); Dec(Cols); Count:=1; forR:=0toRowsdo forC:=0toColsdo begin Mx[R,C]:=Count; Inc(Count); end; end; procedureMatrixMult(Rows,Cols:Integer;constM1,M2:TMatrix;varMx:TMatrix);inline; var I,J,K,Val:Integer; begin Dec(Rows); Dec(Cols); forI:=0toRowsdo forJ:=0toColsdo begin Val:=0; forK:=0toColsdo Inc(Val,M1[I,K]*M2[K,J]); Mx[I,J]:=Val; end; end; var Num,I:Integer; M1,M2,Mx:TMatrix; T1,T2:Double; begin Num:=100000; MakeMatrix(cSize,cSize,M1); MakeMatrix(cSize,cSize,M2); T1:=Now; forI:=0toNumdo MatrixMult(cSize,cSize,M1,M2,Mx); T2:=Now; WriteLn(Mx[0,0],,Mx[2,3],,Mx[3,2],,Mx[4,4],,mx[29,29]); WriteLn(C=,MilliSecondsBetween(T2,T1),ms); end.分别执行10次后结果如下。
Golang:8757879087138748873787448752875287468754,平均:8749.3;
C++:1723173517141707171317251708172317201725,平均:1719.3;
Delphi:2384236223592389236223512340235223562352,平均:2360.7;
在这样的密集运算例子里,Golang的表现实在很差,Golang的编译器优化还有很长的路。而Delphi则不出意外,不温不火,勉强也还算能接受吧。
至此,或许大致可以这样初步评断,Golang在大部分应用场景下在效率方面是满足要求的,而若涉及到密集运算,当前比较好的方法应该是要通过CGo了。考虑到Golang强大的goroutine和channel、丰富的标准库(譬如网络方面)、精简的语法和非常快速的编译速度(几乎媲美Delphi),后端开发尝试下Golang应是比较可行的,而也确实有不少早已用Golang作后端开发的项目实例了。
注:关于Golang的语言语法及并发方面的特性,过段时间再浅叙。
经由Colin同学建议,测试字符串串接中使用的自实现版IntToStr效率不行,对C++很不公平,于是我用回了_itoa_s和_itow_s这俩库函数,如下:
#include"stdafx.h" #include<time.h> #include<stdarg.h> #include<string> #include<iostream> usingnamespacestd; constintcNumMax=1000000; wstringtestW() { wstringret=L""; wchar_tws[10]; for(inti=0;i<cNumMax;i++) { _itow_s(i,ws,10); ret+=ws; } returnret; } stringtest() { stringret=""; chars[10]; for(inti=0;i<cNumMax;i++) { _itoa_s(i,s,10); ret+=s; } returnret; } int_tmain(intargc,_TCHAR*argv[]) { cout<<"Startingtestwithaloopnumof"<<cNumMax<<endl; clock_tt1=clock(); strings=test(); clock_tt2=clock(); cout<<"Result:"<<s.substr(2000,5)<<"..."<<";Size:"<<s.size()<<";Time:"<<t2-t1<<"ms"<<endl; cout<<endl; cout<<"StartingtestforWSTRINGwithaloopnumof"<<cNumMax<<endl; t1=clock(); wstringws=testW(); t2=clock(); wcout<<"Result:"<<ws.substr(2000,5)<<"..."<<";Size:"<<ws.size()<<";Time:"<<t2-t1<<"ms"<<endl; return0; }测试10次,效率果然大幅提升,平均大约分别是:std::string-70ms、std::wstring-75ms,相当快速!不过还是比Delphi慢了40%左右。
本文内容总结:
原文链接:https://www.cnblogs.com/ecofast/p/4043873.html