向量初始化比数组慢...为啥?
Posted
技术标签:
【中文标题】向量初始化比数组慢...为啥?【英文标题】:Vector initializing slower than array...why?向量初始化比数组慢...为什么? 【发布时间】:2009-10-23 17:50:29 【问题描述】:我尝试了两件事:(下面的伪代码)
int arr[10000];
for (int i = 0; i < 10000; i++)
for (int j = 0; j < 10000; j++)
arr[j] = j;
和
vector<int> arr(10000);
for (int i = 0; i < 10000; i++)
for (int j = 0; j < 10000; j++)
arr[j] = j;
我运行了这两个程序并使用“time”shell 命令对其进行计时。程序 1 运行 5 秒,程序 2 运行 30 秒。我在打开编译器优化的情况下运行了这两个程序,并且两个程序的运行时间大约相同(0.38 秒)。我对这些结果感到困惑。有人可以向我解释为什么会这样吗?
谢谢!
【问题讨论】:
您的意思是他们花了 5/30 秒进行优化关闭? 请记住,它们并不完全相同。 Vector默认在堆外分配,但数组在栈上。 嗨 jalf,是的,这是我的问题的一部分。我也对优化后它们如何同时执行感到困惑。 感谢大家的回答。这是一个很棒的社区!我不敢相信我的问题得到了这么快的回答:) 【参考方案1】:对于模板,下标是用 operator[] 完成的。关闭优化后,这通常会生成为真正的函数调用,从而为像下标数组这样简单的事情增加了很多开销。当您开启优化时,它是内联生成的,从而消除了这种开销。
【讨论】:
嗨,杰瑞,谢谢您的回答。现在说得通了。我猜对于数组,使用 [] 不会调用 operator[] 因为它是“自然”(因为缺少更好的词)类型。因此是 5/30。 对——对于内置数组,下标代码几乎肯定总是内联生成。 这强调了函数调用开销是如何真正增加的。 @aip.cd.aish:“内在”和“原生”这两个词比“自然”更常用来描述语言内置的功能。 @Adisak:很酷,谢谢。很高兴知道:)【参考方案2】:在调试模式下,std::vector
的实现提供了大量运行时检查以方便使用。此检查不适用于本机阵列。例如在VC2008中,如果你在调试模式下编译你的vector
例子,operator[]
的情况下会有range-checking
甚至。
【讨论】:
【参考方案3】:如果您的非优化向量实现正在执行边界检查,这将导致差异。
【讨论】:
++ 我猜你是对的,但我想让他(或她)知道。【参考方案4】:这些都是很好的答案,但您可以自己快速找到答案。
您看到了 6 比 1 的性能差异,对吧?只需运行慢速并点击“暂停”按钮。然后查看调用堆栈。六分之五的概率(83%)你会看到它是如何花费额外的 25 秒的。多做几次以获得尽可能多的洞察力。
对于优化的情况,对程序 1 执行相同的操作。由于它比优化的程序慢 13 倍,您将在每次“暂停”时看到原因,概率为 12/13 = 92%。
那是this technique的申请。
【讨论】:
我看到许多开发人员在第一次看到好的 ole' 暂停技术时大吃一惊,尤其是当他们不购买它并花费数小时设置完整的分析运行只是为了发现“暂停”已经确定了。 @DanO:是的。我只是想把这个词说出来。人们没有理由不知道它。 嗨,迈克,感谢您的回复。我想尝试一下,但我正在使用命令行环境和 gcc 来编译我的程序(我是这个环境的新手)。有什么办法可以暂停它并在那里查看堆栈跟踪吗?谢谢:) @aip.cd.aish:我希望这很容易。基本上你需要使用 gdb 得心应手。然后配置它,以便 Control-C 或 Control-Break 中断它但不中止它。坦率地说,在 Windows XP 的 DOS-box 下我没有成功,所以我主要使用 VC 和 VS。在 Unix 下,我曾经使用 adb,并从外部向它发送终止信号。我在 SO 上问过是否有更好的方法,但并没有真正得到好的答案。 ... 我有时会使用一种笨拙的方法。编写一个空函数 foo() 并在整个代码中调用它。在其中放置一个断点,然后禁用它。然后,当它运行时,看看你是否可以在随机时间启用断点。这种方法没有那么精确,但总比没有好。【参考方案5】:因为当你写向量 arr(10000);你创建一个对象,调用它的函数......什么时候它会比你创建 int arr[10000];
【讨论】:
【参考方案6】:在您的示例中,数组位于堆栈上。访问数组中的数据涉及访问堆栈上的数据。真快。
另一方面,当vector
在堆栈上时,std::vector
的数据被分配到其他地方(默认情况下,它通过std::allocator
在堆上分配)。访问vector
中的数据涉及访问堆上的数据。这比访问堆栈上的数据要慢得多。
不过,你会得到一些性能损失。 std::vector
是可增长的,而常规数组则不是。此外,std::vector
的大小不必是编译时间常数,而堆栈上数组的大小则可以。堆分配的数组(通过operator new[]
)不必是编译时常量。如果您将堆分配数组与std::vector
进行比较,您会发现性能更接近。
int* arr = new int[10000];
for (int i = 0; i < 10000; i++)
for (int j = 0; j < 10000; j++)
arr[j] = j;
delete[] arr; // std::vector does this for you
【讨论】:
以上是关于向量初始化比数组慢...为啥?的主要内容,如果未能解决你的问题,请参考以下文章
将数组的列作为向量执行“for循环”时,Cupy 比 numpy 慢