1. 程式人生 > >Golang效能測試與思考

Golang效能測試與思考

本文測試Go、Python、PyPy、C的效率,作為學習Go的參考標準。測試用例:進行(2<<25)次簡單加法

測試環境: 系統:Windows7 專業版 CPU:Intel® Core™ i5-4590 CPU @ 3.30GHZ 3.30GHZ, 14級流水線(Pipeline)

測試用例:進行(2<<25)次簡單加法

// golang example
package main

import "fmt"
import "time"

func main() {
    var N int = 2 << 25
    var j int = 0

    t0 :=
time.Now() for i := 1; i < (N); i++ { j += 1 } elapsed := time.Since(t0) fmt.Println("N:", N, "time:", elapsed) }

測試結果:N: 67108864 time: 20.5027ms

# -*- coding: utf-8 -*-
# Python and Pypy example

import time

t0 = time.clock()

N = 2 << 25
j = 0

for i in xrange(N):
    j += 1

print "N:{}, time:{:.2f}ms".format(N, (time.clock() - t0) * 1000)

測試結果: Python N:67108864, time:5102.14ms PyPy: N:67108864, time:124.16ms

#include "stdafx.h"
#include<stdlib.h>
#include "test1.h"

#include<stdio.h>
#include<time.h>


#ifdef _WIN32
#include <Windows.h>
#include <sys/timeb.h>
#elif defined __linux__
#include <time.h>
#endif

 unsigned long ClockMs64()
{
#ifdef _WIN32
    struct __timeb64 timebuffer;
    _ftime64_s(&timebuffer);
    return timebuffer.time * 1000 + timebuffer.millitm;

#else
    timespec ts;
    if (-1 == clock_gettime(CLOCK_REALTIME, &ts))
    {
        return 0;
    }
    return ((unsigned long)ts.tv_sec) * 1000 + ts.tv_nsec/1000000;
#endif
}

 unsigned long Clock()
{
    return (unsigned long)ClockMs64();
}

int _tmain(int argc, _TCHAR* argv[])
{
    UINT64 N =  2 << 25;
    UINT64 j = 1;
    unsigned long start = Clock();

    for(UINT64 i=0;i<N;i++)
    {
        j += 1;
    }
    unsigned long end = Clock();
    printf("N:%I64d, time:%dms\n", N, end-start);
    return 0;
}

測試結果:N:67108864, time:160ms

綜合以上測試結果:

時間
C 160ms
PyPy 124.16ms
Python 5102.14ms
Golang 20.50ms

結論:從測試結果來看,執行效率Golang > PyPy > C > Python 分析: Golang是編譯型語言,會將程式碼編譯成二進位制可執行機器碼。我們來分析執行Golang的執行時間,我們知道一個彙編指令至少執行一個CPU週期(指令執行整數個週期,大部分指令執行一個週期,指令具體執行週期可以參考CPU手冊)。也就是說只要我們知道執行整個程式的彙編指令數量,我們就能對程式執行時間做出評判。

測試用例迴圈體內執行以下操作,大致翻譯成彙編指令,統計彙編指令數目
1. i < N --> 將i、N入棧,cmp後比較結果來做Jmp操作,大約5條彙編指令;
2. j += 1 --> 將j入棧,執行Inc操作,然後儲存到j,大約3條彙編指令;
3. i ++ --> 將i入棧,執行Inc,儲存到i,大約3條彙編;
4. 還有一個隱藏的Jump到操作1的操作, 1條彙編

故一個迴圈體總體彙編指令條目為(5+3+3+1)=12條

CPU主頻為3.30GHZ,迴圈體彙編條目約為12條。我們以CPU滿載、每條指令一個週期計算,執行2<<25次迴圈體需要用時=1000 * ((2<<25) * 12) / (3.3 * (10**9)) = 244.03ms。 而我們測試Golang用時20.50ms,為什麼Golang比前面計算的244.03ms遠遠要小?祕密在於CPU的指令流水線,根據資料我們知道Intel CPU是14級流水線架構,也就是說一個CPU週期最大可執行14條彙編指令。我們用單級流水線理論時間除以Golang用時:244.03ms / 20.50ms = 11.9,考慮多CPU週期彙編指令的存在和系統排程消耗,14是我們理論上限值,可見結果符合預期。

Golang在這個測試用例中比C語言更快,更是秒殺Python,超乎預期。通過計算髮現Golang的計算效率接近CPU的極限,著實是一門高效的編譯型語言。