Go和C+中的矢量性能+; 请考虑GO和C++ 11中的这两个片段。在C++中代码> STD::vector < /Cord>是一个加倍数组,它已摊销了O(1)插入操作。如何在GO中实现相同的性能?问题是,这个GO代码在我的硬件上大约慢了3倍。跑很多次

Go和C+中的矢量性能+; 请考虑GO和C++ 11中的这两个片段。在C++中代码> STD::vector < /Cord>是一个加倍数组,它已摊销了O(1)插入操作。如何在GO中实现相同的性能?问题是,这个GO代码在我的硬件上大约慢了3倍。跑很多次,c++,performance,c++11,vector,go,C++,Performance,C++11,Vector,Go,汇编: go build vec.go(go版本go1.2.1 linux/amd64) g++-O2-std=gnu++11-o vec vec.cc(g++(Ubuntu 4.8.2-19ubuntu1)4.8.2) GO版本(vec.GO): package main type X struct { x int32 y float64 } const N int = 80000000 func main() { x := X{123, 2.64}

汇编:

  • go build vec.go
    (go版本go1.2.1 linux/amd64)
  • g++-O2-std=gnu++11-o vec vec.cc
    (g++(Ubuntu 4.8.2-19ubuntu1)4.8.2)
GO版本(vec.GO):

package main

type X struct {
    x int32
    y float64
}

const N int = 80000000

func main() {
    x := X{123, 2.64}
    s := make([]X, 1)
    for i := 0; i < N; i++ {
        s = append(s, x)
    }
}
主程序包
X型结构{
x int32
y浮动64
}
常数N int=8000000
func main(){
x:=x{123,2.64}
s:=make([]X,1)
对于i:=0;i
C++11版本(vec.cc):

#包括
常数整数N=80000000;
结构X{
int x;
双y;
};
内部主(空)
{
X{123,2.64};
std::向量s(1);
对于(int i=0;i
如果您事先知道元素的数量,可以通过以下方式预先分配:

s := make([]X, 0, N)
for i := 0; i < N; i++ {
    s = append(s, x)
}
s:=make([]X,0,N)
对于i:=0;i
同样使用Go1.3,编译器得到了一些优化


为了更好地实现矢量化,请尝试使用
gccgo

Go的规范,它不需要
append()
有任何特殊的复杂性,但实际上,它也是以amortized常量时间实现的,如对的回答中所述

当前的实现如下:对于小于1024的数组大小,它会根据需要翻倍,而大于1024的数组大小会增加到原始大小的1.25倍。增加1.25倍仍然是摊余固定时间,但其效果是施加比始终加倍的实现更高的摊余固定系数。但是,1.25倍的内存总的来说消耗更少

如果您只获得了几次不同的性能行为(即使是非常大的N),那么您将看到不同的常量因素在起作用。我注意到,
gc
编译器生成的机器代码比
gccgo
生成的机器代码效率高很多


要亲自验证Go是否在氨化恒定时间内运行,请尝试绘制运行算法所需的时间,以获得几个不同的N值。

我已经回答了您的计算复杂性问题:。按固定时间摊销

我的结果来自你的基准测试

$ rm vec
$ cat vec.cc
#include <vector>

const int N = 80000000;

struct X {
        int x;
        double y;
};

int main(void)
{
        X x{123, 2.64};
        std::vector<X> s(1);
        for (int i = 0; i < N; ++i) {
                s.push_back(x);
        }
}
$ g++ -O2 -std=gnu++11 -o vec vec.cc
$ time ./vec
real    0m1.360s
user    0m0.536s
sys 0m0.816s
$ rm vec
$ cat vec.go
package main

type X struct {
    x int32
    y float64
}

const N int = 80000000

func main() {
    x := X{123, 2.64}
    s := make([]X, 1)
    for i := 0; i < N; i++ {
        s = append(s, x)
    }
}
$ go version
go version devel +6b696a34e0af Sun Aug 03 15:14:59 2014 -0700 linux/amd64
$ go build vec.go
$ time ./vec
real    0m2.590s
user    0m1.192s
sys 0m1.388s
$ 
$rm vec
$cat vec.cc
#包括
常数整数N=80000000;
结构X{
int x;
双y;
};
内部主(空)
{
X{123,2.64};
std::向量s(1);
对于(int i=0;i我不知道GO,但是如果你关心性能,C++代码应该在循环之前调用<代码> Reals。@ Jalf不,我希望基准与相同大小的初始数组1相同。C++版本相当快。但是Go可能没有使用双倍数组算法。当然,但是比较低效代码和低效代码并不能告诉你什么有用的东西。这就像编译时没有启用优化一样。如果要比较哪一个更快,两个变体都应该写得更快。@jalf更改保留大小不会改变任何东西。你认为什么是低效的?预先保留尺码会改变性能。它避免了重新分配内存和复制对象。这是摊销O(1)和实际O(1)复杂度之间的差异。Thx,但不是,
N
仅用于测试目的。让我们假设在常规设置中,您只追加和追加,不知道会持续多长时间。您是否尝试过go1.3和gccgo?是的,
go1.3
速度大致相同,
gccgo-4.9
再慢3倍!我只是好奇,你对gccgo使用了什么标志?如果你用过的话。终于!回答得很好。似乎大多数
std::vector
实现每次都会加倍,因此
1.25x
因子参数似乎是完美的。谢谢。@eeq:大多数
vector
实现(我已经看过了)使用固定因子,但我最近看到的都是1.5倍而不是两倍。“浪费更少的内存”——更具体地说,在现代操作系统/商品桌面/服务器硬件上,它浪费更少的虚拟地址空间,在64位程序中,这是第一次完全免费分配,但对于32位程序来说,这是一个潜在的问题。即使对于64位应用程序,当虚拟地址空间已经由RAM支持时,这仍然可能是一个问题,这是早期动态内存使用的副作用-超过峰值
size()
的后续容量可能最终被推到交换位置,即使当前的动态内存请求没有触及它。@TonyD某些操作系统(如Linux)默认情况下,不要过度使用内存。浪费虚拟地址空间可能是问题,也可能不是问题。@FUZxxl:Linux默认不使用吗?无论如何,“可能是问题,也可能不是问题”是我在上面强调的全部内容,有一点理由/见解支持它。