更新:似乎这是MSVC的一个错误,但它也发生在ICC版本14上,并且启用了最高优化(/ o3)。
UPDATE2:随着ICC的优化关闭,我得到了:
我使用下面的代码比较std :: array vs raw array vs std :: vector的性能。我已经在Win 7 64上使用MSVC 2012编译器和英特尔编译器vs 14进行了测试,并进行了64位编译。 CPU是Intel第3代。
结果是(一致地):
当我检查程序集时,编译器为std::array
和原始数组选择XMM寄存器,因此可能正在进行某种SIMD处理?但是,对于std::vector
,使用了常规的r8-r15寄存器。
假设我对上述内容是正确的,为什么XMM寄存器不能用于std :: vector?
以下是完整工作的测试代码(您需要增加默认堆栈保留大小):
#include <iostream>
#include <vector>
#include <array>
const unsigned int noElements = 10000000;
const unsigned int noIterations = 500;
void testVector(){
volatile unsigned long long sum = 0;
unsigned long long start = 0;
unsigned long long finish = 0;
unsigned int x;
unsigned int y;
std::vector<unsigned int> vec;
vec.resize(noElements);
start = __rdtscp(&x);
for(int i=0; i<noIterations; i++){
for(int i=0; i<noElements; i++){
vec[i] = i;
}
for(int i=0; i<noElements; i++){
sum += (3 * vec[i]);
}
}
finish = __rdtscp(&y);
std::cout << "std::vector:\t" << (finish - start)/1000000 << std::endl;
}
void testRawArray(){
volatile unsigned long long sum = 0;
unsigned long long start = 0;
unsigned long long finish = 0;
unsigned int x;
unsigned int y;
unsigned int myRawArray[noElements];
start = __rdtscp(&x);
for(int i=0; i<noIterations; i++){
for(int i=0; i<noElements; i++){
myRawArray[i] = i;
}
for(int i=0; i<noElements; i++){
sum += (3 * myRawArray[i]);
}
}
finish = __rdtscp(&y);
std::cout << "raw array: \t" << (finish - start)/1000000 << std::endl;
}
void testStdArray(){
volatile unsigned long long sum = 0;
unsigned long long start = 0;
unsigned long long finish = 0;
unsigned int x;
unsigned int y;
std::array<unsigned int, noElements> myStdArray;
start = __rdtscp(&x);
for(int i=0; i<noIterations; i++){
for(int i=0; i<noElements; i++){
myStdArray[i] = i;
}
for(int i=0; i<noElements; i++){
sum += (3 * myStdArray[i]);
}
}
finish = __rdtscp(&y);
std::cout << "std::array: \t" << (finish - start)/1000000 << std::endl;
}
int main(){
testStdArray();
testRawArray();
testVector();
}
答案 0 :(得分:4)
以下是我的计算机上的结果,使用gcc 4.9和Intel C ++编译器14.我已将代码更改为noElements = 1000000和noIterations = 1000.此外,我使用std::chrono::steady_clock
来计算循环时间
fayard@speed:Desktop$ uname -a
Darwin speed.home 13.2.0 Darwin Kernel Version 13.2.0: Thu Apr 17 23:03:13 PDT 2014; root:xnu-2422.100.13~1/RELEASE_X86_64 x86_64
fayard@speed:Desktop$ g++-4.9 --version
g++-4.9 (Homebrew gcc49 4.9.0 --enable-all-languages) 4.9.0
Copyright (C) 2014 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
fayard@speed:Desktop$ g++-4.9 -std=c++11 -Ofast main-array.cpp -o main
fayard@speed:Desktop$ ./main
std::array: 1891738
raw array: 1889974
std::vector: 1891721
fayard@speed:Desktop$ icpc --version
icpc (ICC) 14.0.3 20140415
Copyright (C) 1985-2014 Intel Corporation. All rights reserved.
fayard@speed:Desktop$ icpc -std=c++11 -Ofast main-array.cpp -o main
fayard@speed:Desktop$ ./main
std::array: 1896141
raw array: 1886859
std::vector: 2135880
如您所见,gcc 4.9没有区别。对于英特尔编译器,std :: vector比其他的慢。如果检查汇编代码,就会发现循环
for(int i=0; i<noElements; i++){
vec[i] = i;
}
为C数组,std :: array而不是std :: vector进行矢量化。如果你问英特尔编译器为什么,你就会得到
main-array.cpp(23): (col. 9) remark: loop was not vectorized: existence of vector dependence
当然,在这个循环中没有依赖,但编译器无法弄明白。矢量化对于标准库来说是一种痛苦,其中所有这些容器都需要访问元素的方法,并且这些方法隐藏了一些指针算法。它使优化成为编写优化编译器的人的噩梦。 因此,您在此处看到的内容在很大程度上取决于您使用的编译器。最有可能的是,您会发现版本之间的变化。
你对“完美”编译器的期望与gcc几乎相同。你不应该发现C数组和std :: array之间有任何区别(它们都在堆栈上分配),当你处理庞大的数组时,你不应该看到C数组和std :: vector之间有任何区别。分配时间不是CPU占用时间的地方。 另一方面,如果你比较大量的小数组的分配和释放(例如一些大小为3的std :: array),std :: array或C-array会将std :: vector吹出water,因为分配时间(在std :: vector的堆上)很重要。
所以要吸取的教训是:
在C ++ 11中,忘记了C-arrays(*)
将std :: array用于小数组(其大小在编译时已知)和std :: vector用于大数组
如果你需要一个巨大的堆栈,很可能你做的事情很愚蠢
Fortran岩石,因为它没有所有这些问题。它有一种类型的数组thtat可以在堆栈上分配,堆可以执行(或不执行)绑定检查以及哪些矢量化器可以工作,因为它们不必处理疯狂的指针。
现在,回到你的问题:为什么编译器将XMM寄存器用于raw / std数组但不使用向量?
通过gcc可以看到并非总是如此。原因是为C ++编写优化编译器是一个巨大的痛苦,许多编译器仍然没有做很多“基本”优化。
(*):仍然存在有用的极端情况。