|
从接触 Golang 开始,断断续续已有差不多一年左右的时间了,都是业余自己学学看看,尚主要限于语法及语言特性,还没有用它写过实际的项目。
关于 Golang 的语法及语言特性,网上有很多资源可以学习。后面某个时间,我也许会写一篇粗浅的文章,来比较一下 Golang 和 C++、Delphi 甚至 C# 等语言语法方面的特性。
我算是个急性子的人(当然现在好一些了),于是作为码农,显而易见会对“效率”比较敏感。这里的效率不单单指编译器生成的机器码优化程度,也包括编译器的编译速度,所以我对 C++ 兴趣不算大,虽然它是我平时的工作语言。
言归正传。
分别用 Golang、C++、Delphi 写了四个小例子,包括普通的应用场景、字符串(串接)操作及数据密集计算(当然也会涉及到譬如库函数的优化等)。我的电脑软硬件环境为:Win7 64bit,Xeon E3-1230(8核),16G RAM。Golang 版本是 1.3.1 Windows/386,VC 则用的 VS 2012,而 Delphi 则用的 XE6 Update1。VC 和 Delphi 编译设置为 Win32 &> 所有测试计量单位均为毫秒(ms)。
首先是计算 π 的例子,代码分别如下。
Golang:
package main
import (
"fmt"
"time"
)
const cNumMax = 999999999
func main() {
sign := 1.0
pi := 0.0
t1 := time.Now()
for i := 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
#include
int _tmain(int argc, _TCHAR* argv[])
{
const int cNumMax = 999999999;
double sign = 1.0;
double pi = 0;
clock_t t1 = clock();
for (int i = 1; i < cNumMax + 2; i += 2)
{
pi += (1.0f / (double)i) * sign;
sign = -sign;
}
pi *= 4;
clock_t t2 = clock();
printf("PI = %lf; Time = %d\n", pi, t2 - t1);
return 0;
}
Delphi:
program PiCalcer;
{$APPTYPE CONSOLE}
{$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;
while I < cNumMax + 2 do
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:2038 2028 2036 2024 2034 2015 2034 2018 2024 2018,平均:2026.9;
C++ :2041 2052 2062 2036 2033 2049 2039 2026 2037 2038,平均:2041.3;
Delphi :2594 2572 2574 2584 2574 2564 2575 2575 2571 2563,平均:2574.6。
结果居然很不错,比 VC 还快,而 Delphi,大家都懂,优化向来不是它的“强项”。
然后是个质数生成例子。
Golang:
package main
import (
"fmt"
"time"
)
const cNumMax = 10000000
func main() {
t1 := time.Now()
var nums [cNumMax + 1]int
var i, j int
for i = 2; i < cNumMax+1; i++ {
nums = i
}
for i = 2; i < cNumMax+1; i++ {
j = 2
for j*i < cNumMax+1 {
nums[j*i] = 0
j++
}
}
cnt := 0
for i = 2; i < cNumMax+1; i++ {
if nums != 0 {
cnt++
}
}
t2 := time.Now()
fmt.Println("Time:", t2.Sub(t1), " Count:", cnt)
}
C++:
#include "stdafx.h"
#include
#include
const int cNumMax = 10000000;
int _tmain(int argc, _TCHAR* argv[])
{
clock_t t1 = clock();
int *nums = (int*)malloc(sizeof(int) * (cNumMax + 1));
int i;
for (i = 2; i < cNumMax + 1; i++)
{
nums = i;
}
int j;
for (i = 2; i < cNumMax + 1; i++)
{
j = 2;
while (j * i < cNumMax + 1)
{
nums[j * i] = 0;
j++;
}
}
int cnt = 0;
for (i = 2; i < cNumMax + 1; i++)
{
if (nums != 0)
{
cnt++;
}
}
free(nums);
clock_t t2 = clock();
printf("Time: %dms; Count: %d\n", t2 - t1, cnt);
}
Delphi:
program PrimeSieve;
{$APPTYPE CONSOLE}
{$R *.res}
uses
System.SysUtils, System.DateUtils;
const
cNumMax = 10000000;
var
T1, T2: Double;
I, J : Integer;
Cnt : Integer;
Nums : array of Integer;
begin
T1 := Now;
SetLength(Nums, cNumMax + 1);
for I := 2 to cNumMax do
Nums[I] := I;
for I := 2 to cNumMax do
begin
J := 2;
while J * I < cNumMax + 1 do
begin
Nums[J * I] := 0;
Inc(J);
end;
end;
Cnt := 0;
for I := 2 to cNumMax do
begin
if Nums[I] 0 then
Inc(Cnt);
end;
SetLength(Nums, 0);
T2 := Now;
Writeln(Format('Cnt = %d; Time = %d', [Cnt, MilliSecondsBetween(T2, T1)]));
Readln;
end.
同样分别执行 10 次,结果如下。
Golang:959 957 959 953 961 951 948 956 956 956,平均:955.6;
C++ :965 965 967 953 961 964 963 960 956 956,平均:961;
Delphi : 973 976 973 982 981 970 977 979 971 977,平均:975.9;
仍然,Golang 看上去最快,而 Delphi 则很正常地居末。
所以我忍不住想要来一个能展现 Delphi 优点的例子,这个例子几乎毫无疑问,和字符串操作(及内存管理器)相关,所以有如下字符串串接的示例(其中涉及到了譬如 IntToStr / itoa 这样的函数调用,我自己实现了个 C++ 版的 IntToStr)。
Golang:
package main
import (
"bytes"
"fmt"
"strconv"
"time"
)
const cNumMax = 1000000
// bytes.Buffer(7.2.6)
func testViaBuffer() string {
var buf bytes.Buffer
for i := 0; i < cNumMax; i++ {
buf.WriteString(strconv.Itoa(i))
}
return buf.String()
}
// +=
func testViaNormal() string {
var ret string
for i := 0; i < cNumMax; i++ {
ret += strconv.Itoa(i)
}
return ret
}
func main() {
fmt.Println("Test via bytes.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("Test via normal way...")
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
#include
#include
#include
using namespace std;
const int cNumMax = 1000000;
wstring FormatV(const wchar_t* pwcFormat, va_list argList)
{
wstring ws;
int nLen = _vscwprintf(pwcFormat, argList);
if (nLen > 0)
{
ws.resize(nLen);
vswprintf_s(&ws[0], nLen + 1, pwcFormat, argList);
}
return ws;
}
wstring __cdecl Format(const wchar_t* pwcFormat, ...)
{
va_list argList;
va_start(argList, pwcFormat);
wstring ws = FormatV(pwcFormat, argList);
va_end(argList);
return ws;
}
string FormatVA(const char* pcFormat, va_list argList)
{
string s;
int nLen = _vscprintf(pcFormat, argList);
if (nLen > 0)
{
s.resize(nLen);
vsprintf_s(&s[0], nLen + 1, pcFormat, argList);
}
return s;
}
string __cdecl FormatA(const char* pcFormat, ...)
{
va_list argList;
va_start(argList, pcFormat);
string s = FormatVA(pcFormat, argList);
va_end(argList);
return s;
}
wstring IntToStr(int nValue)
{
return Format(L"%d", nValue);
}
string IntToStrA(int nValue)
{
return FormatA("%d", nValue);
}
wstring testW()
{
wstring ret = L"";
for (int i = 0; i < cNumMax; i++)
{
ret += IntToStr(i);
}
return ret;
}
string test()
{
string ret = "";
for (int i = 0; i < cNumMax; i++)
{
ret += IntToStrA(i);
}
return ret;
}
int _tmain(int argc, _TCHAR* argv[])
{
cout |
|