亚洲在线久爱草,狠狠天天香蕉网,天天搞日日干久草,伊人亚洲日本欧美

為了賬號安全,請及時綁定郵箱和手機立即綁定
已解決430363個問題,去搜搜看,總會有你想問的

go 和 c++ 之間的地圖性能比較

go 和 c++ 之間的地圖性能比較

Go
POPMUISE 2023-06-05 18:29:52
我不明白golang怎么在這個操作上比c++快10倍,甚至go中的map查找比c++快3倍。這是 C++ 片段#include <iostream>#include <unordered_map>#include <chrono>std::chrono::nanoseconds elapsed(std::chrono::steady_clock::time_point start) {    std::chrono::steady_clock::time_point now = std::chrono::high_resolution_clock::now();    return std::chrono::duration_cast<std::chrono::nanoseconds>(now - start);}void make_map(int times) {    std::unordered_map<double, double> hm;    double c = 0.0;    for (int i = 0; i < times; i++) {        hm[c] = c + 10.0;        c += 1.0;    }}int main() {    std::chrono::steady_clock::time_point start_time = std::chrono::high_resolution_clock::now();    make_map(10000000);    printf("elapsed %lld", elapsed(start_time).count());}這是 golang 片段:func makeMap() {    o := make(map[float64]float64)    var i float64 = 0    x := time.Now()    for ; i <= 10000000; i++ {        o[i] = i+ 10    }    TimeTrack(x)}func TimeTrack(start time.Time) {    elapsed := time.Since(start)    // Skip this function, and fetch the PC and file for its parent.    pc, _, _, _ := runtime.Caller(1)    // Retrieve a function object this functions parent.    funcObj := runtime.FuncForPC(pc)    // Regex to extract just the function name (and not the module path).    runtimeFunc := regexp.MustCompile(`^.*\.(.*)$`)    name := runtimeFunc.ReplaceAllString(funcObj.Name(), "$1")    log.Println(fmt.Sprintf("%s took %s", name, elapsed))}我想知道的是如何優化 c++ 以獲得更好的性能。
查看完整描述

3 回答

?
蝴蝶不菲

TA貢獻1810條經驗 獲得超4個贊

很難確定“C++ 的速度”(對于幾乎任何特定事物),因為它可能取決于相當多的變量,例如您使用的編譯器。例如,對于此代碼的 C++ 版本,我通常會看到 gcc 和 msvc 之間存在 2:1 左右的差異。

至于 C++ 和 Go 之間的差異,我猜這主要歸結于哈希表的實現方式的差異。一個明顯的一點是 Go 的 map 實現一次以 8 個元素的塊為單位分配數據空間。至少我見過的標準庫實現,std::unordered_map每個塊只放置一個項目。

我們希望這意味著在典型情況下,C++ 代碼將從堆/空閑存儲中進行大量的單獨分配,因此它的速度將更多地取決于堆管理器的速度。Go 版本還應該具有更高的引用位置,以便更好地使用緩存。

考慮到這些差異,我有點驚訝您只看到 10:1 的差異。我的直接猜測會(稍微)高于這個數值——但眾所周知,一次測量值超過 100 次猜測。


查看完整回答
反對 回復 2023-06-05
?
繁花不似錦

TA貢獻1851條經驗 獲得超4個贊

已更新以測量cpp和的類似操作go。它在調用制圖函數之前開始測量,并在函數返回時結束測量。兩個版本都在地圖中保留空間并返回創建的地圖(從中打印了幾個數字)。


稍作修改cpp:


#include <iostream>

#include <unordered_map>

#include <chrono>


std::unordered_map<double, double> make_map(double times) {

    std::unordered_map<double, double> m(times);


    for (double c = 0; c < times; ++c) {

        m[c] = c + 10.0;

    }

    return m;

}


int main() {

    std::chrono::high_resolution_clock::time_point start_time = std::chrono::high_resolution_clock::now();

    auto m = make_map(10000000);

    std::chrono::high_resolution_clock::time_point end_time = std::chrono::high_resolution_clock::now();

    auto elapsed = std::chrono::duration_cast<std::chrono::nanoseconds>(end_time-start_time);

    std::cout << elapsed.count()/1000000000. << "s\n";

    std::cout << m[10] << "\n"

              << m[9999999] << "\n";    

}


% g++ -DNDEBUG -std=c++17 -Ofast -o perf perf.cpp

% ./perf

2.81886s

20

1e+07

稍作修改的go版本:


package main


import (

    "fmt"

    "time"

)


func make_map(elem float64) map[float64]float64 {

    m := make(map[float64]float64, int(elem))

    var i float64 = 0

    for ; i < elem; i++ {

        m[i] = i + 10

    }

    return m

}


func main() {

    start_time := time.Now()

    r := make_map(10000000)

    end_time := time.Now()

    fmt.Println(end_time.Sub(start_time))

    fmt.Println(r[10])

    fmt.Println(r[9999999])

}


% go build -a perf.go

% ./perf

1.967707381s

20

1.0000009e+07

它看起來不像更新前那樣平局。使 cpp 版本變慢的一件事是double. 當用一個非常糟糕(但很快)的哈希器替換它時,我把時間降到了 1.89489s。


struct bad_hasher {

    size_t operator()(const double& d) const {

        static_assert(sizeof(double)==sizeof(size_t));


        return

            *reinterpret_cast<const size_t*>( reinterpret_cast<const std::byte*>(&d) );

    }

};


查看完整回答
反對 回復 2023-06-05
?
大話西游666

TA貢獻1817條經驗 獲得超14個贊

無意義的微基準測試產生無意義的結果。


繼續@mrclx和@TedLyngmo的微基準線程,修復@TedLyngmo 的 Go 微基準中的錯誤:

perf.go:

package main


import (

? ? "fmt"

? ? "time"

)


func makeMap(elem float64) time.Duration {

? ? x := time.Now()

? ? o := make(map[float64]float64, int(elem))

? ? var i float64 = 0

? ? for ; i < elem; i++ {

? ? ? ? o[i] = i + 10

? ? }

? ? t := time.Now()

? ? return t.Sub(x)

}


func main() {

? ? r := makeMap(10000000)

? ? fmt.Println(r)

}

輸出:


$ go version

go version devel +11af353531 Tue Feb 12 14:48:26 2019 +0000 linux/amd64

$ go build -a perf.go

$ ./perf

1.649880112s

$?

perf.cpp:


#include <iostream>

#include <unordered_map>

#include <chrono>


void make_map(double times) {

? ? std::unordered_map<double, double> hm;

? ? hm.reserve(static_cast<size_t>(times)); // <- good stuff


? ? for (double c = 0; c < times; ++c) {

? ? ? ? hm[c] = c + 10.0;

? ? }

}


int main() {

? ? std::chrono::high_resolution_clock::time_point start_time = std::chrono::high_resolution_clock::now();

? ? make_map(10000000);

? ? std::chrono::high_resolution_clock::time_point end_time = std::chrono::high_resolution_clock::now();

? ? auto elapsed = std::chrono::duration_cast<std::chrono::nanoseconds>(end_time-start_time);

? ? std::cout << elapsed.count()/1000000000. << "s\n";

}

輸出:


$ g++ --version

g++ (Ubuntu 8.2.0-7ubuntu1) 8.2.0

$ g++ -DNDEBUG -std=c++17 -Ofast -o perf perf.cpp

$ ./perf

3.09203s

$?


查看完整回答
反對 回復 2023-06-05
  • 3 回答
  • 0 關注
  • 274 瀏覽
慕課專欄
更多

添加回答

舉報

0/150
提交
取消
微信客服

購課補貼
聯系客服咨詢優惠詳情

幫助反饋 APP下載

慕課網APP
您的移動學習伙伴

公眾號

掃描二維碼
關注慕課網微信公眾號