我在对循环缓冲区进行基准测试时偶然发现了这一点。任何人都可以解释一下std :: vector如何在这个实例中胜过普通数组吗?
#include <iostream>
#include <vector>
struct uint_pair {
unsigned int a, b;
uint_pair (unsigned int x = 0, unsigned int y = 0) : a(x), b(y) {}
};
struct container {
unsigned int pos;
#ifdef USE_VECTOR
std::vector<uint_pair> data;
container() : pos(0) { data.resize(16); }
#else
uint_pair data[16];
container() : pos(0) {}
#endif
void add(uint_pair val) {
data[++pos % 16] = val;
}
};
int main() {
container c;
for (unsigned int i = 0; i < 1000000000; i++) c.add(uint_pair{i, i});
std::cout << c.data[0].a << " " << c.data[0].b << std::endl;
}
这些是我使用GCC的结果(与Clang类似):
g++ -o bench -std=c++0x -Os main.cpp -D'USE_VECTOR'
real 0m8.757s
user 0m8.750s
sys 0m0.002s
g++ -o bench -std=c++0x -Os main.cpp
real 0m9.215s
user 0m9.209s
sys 0m0.002s
答案 0 :(得分:9)
以下是如何消除差异的方法。而不是你的add
,使用这样的函数:
void set(unsigned int x, unsigned int y) {
++pos;
data[pos % 16].a = x;
data[pos % 16].b = y;
}
这样叫:
for (unsigned int i = 0; i < 1000000000; i++) c.set(i, i);
这与您的完全相同,但它避免了语义上创建临时对象。看起来当你使用向量时,编译器能够更好地优化临时值。
$ g++-4.8 -o bench -std=c++11 -Os main.cpp -DUSE_VECTOR
$ time ./bench
999999999 999999999
real 0m0.635s
user 0m0.630s
sys 0m0.002s
$ g++-4.8 -o bench -std=c++11 -Os main.cpp
$ time ./bench
999999999 999999999
real 0m0.644s
user 0m0.639s
sys 0m0.002s
在我的机器上,set
和add
方法都会产生与矢量相同的性能。只有阵列显示出差异。为了进一步提高优化能力,如果使用-O0进行编译,那么数组方法会稍快一些(但是比使用-Os慢10倍)。
这并不能解释为什么编译器对这两者的区别对待。毕竟,矢量是由数组支持的。此外,std::array
的行为与您的C风格数组完全相同。
答案 1 :(得分:2)
一个问题是放置&#34; pos&#34;结构中的成员。
对于c-array,请记住它是连续存储在与你的&#34; pos&#34;相邻的内存中。会员。当数据被推入到c-array中时,必须发出额外的指令以偏移到结构中,而不是&#34; pos&#34;会员。但是,写入向量不会产生这样的限制,因为它的内存位于其他地方。
要提高性能,请确保最热门的数据位于缓存行的前面。
修改强>
要使c数组的执行速度与向量一样快,必须在64位机器上的8字节边界上分配c数组。如下所示:
uint_pair* data;
unsigned int pos;
container() : pos(0) {
std::size_t bufSize = sizeof(uint_pair) * 17;
void* p = new char[bufSize];
p = std::align(8, sizeof(uint_pair), p, bufSize);
data = reinterpret_cast<uint_pair*>(p);
}
稍加修改的添加功能:
void add(unsigned int x, unsigned int y) {
auto& ref = data[pos++ % 16];
ref.a = x;
ref.b = y;
}
c-array现在时间:
real 0m0.735s
user 0m0.730s
sys 0m0.002s
和std :: vector:
real 0m0.743s
user 0m0.736s
sys 0m0.004s
标准库实施者正在为您提供全程服务:)
答案 2 :(得分:0)
由于operator =(rvalue reference),C ++ 11编译器似乎为vector生成了更好的代码。 首先,在C ++ 03编译器中,普通数组比矢量快两倍。 其次,如果使用Adam建议的void set(unsigned int x,unsigned int y),则没有区别。
向量的汇编代码
.L49:
leal (%rdi,%rax), %esi
andl $15, %esi
leaq (%rdx,%rsi,8), %rsi
movl %eax, (%rsi)
movl %eax, 4(%rsi)
incq %rax
cmpq $1000000000, %rax
jne .L49
for plain array
.L3:
movl 12(%rsp), %edx
incl %edx
movl %edx, 12(%rsp)
andl $15, %edx
leaq 12(%rsp,%rdx,8), %rdx
movl %eax, 4(%rdx)
movl %eax, 8(%rdx)
incl %eax
cmpl $1000000000, %eax
jne .L3