我试图使用Eratosthenes' Sieve算法找到所有不大于n
的质数,我有以下代码,在矢量和C数组中实现了筛,我发现差不多在所有的时间里,C阵列总是更快。
使用vector:
int countPrimes_vector(int n) {
int res = 0;
vector<char>bitmap(n);
memset(&bitmap[0], '1', bitmap.size() * sizeof( bitmap[0]));
//vector<bool>bitmap(n, true); Using this one is even slower!!
for (int i = 2; i<n; ++i){
if(bitmap[i]=='1')++res;
if(sqrt(n)>i)
{
for(int j = i*i; j < n; j += i) bitmap[j] = '0';
}
}
return res;
}
使用C数组:
int countPrimes_array(int n) {
int res = 0;
bool * bitmap = new bool[n];
memset(bitmap, true, sizeof(bool) * n);
for (int i = 2; i<n; ++i){
if(bitmap[i])++res;
if(sqrt(n)>i)
{
for(int j = i*i; j < n; j += i) bitmap[j] = false;
}
}
delete []bitmap;
return res;
}
测试代码:
clock_t t;
t = clock();
int a;
for(int i=0; i<10; ++i)a = countPrimes_vector(8000000);
t = clock() - t;
cout<<"time for vector = "<<t<<endl;
t = clock();
int b;
for(int i=0; i<10; ++i)b = countPrimes_array(8000000);
t = clock() - t;
cout<<"time for array = "<<t<<endl;
输出:
time for vector = 32460000
time for array = 29840000
我已经测试了很多次,而C数组总是更快。它背后的原因是什么?
我经常听说vector
和C数组的性能是相同的,vector
应始终用作标准容器。这个陈述是真的,还是至少一般来说?在什么情况下C阵列应该是首选?
修改
正如以下评论所示,在启用优化-O2
或-O3
(最初使用g++ test.cpp
编译)后,vector
和C数组之间的时差为不再有效,在某些情况下vector
比C数组快。
答案 0 :(得分:7)
您的比较包含可以解释差异的不一致性,另一个因素可能是没有充分优化的编译结果。有些实现在STL的调试版本中有很多额外的代码,例如,MSVC会对向量元素访问进行检查,从而显着降低调试版本的速度。
以下代码显示两者之间的性能更接近,差异可能只是缺少样本(ideone的超时限制为5秒)。
#include <vector>
#include <cmath>
#include <cstring>
int countPrimes_vector(int n) {
int res = 0;
std::vector<bool> bitmap(n, true);
for (int i = 2; i<n; ++i){
if(bitmap[i])
++res;
if(sqrt(n)>i)
{
for(int j = i*i; j < n; j += i) bitmap[j] = false;
}
}
return res;
}
int countPrimes_carray(int n) {
int res = 0;
bool* bitmap = new bool[n];
memset(bitmap, true, sizeof(bool) * n);
for (int i = 2; i<n; ++i){
if(bitmap[i])++res;
if(sqrt(n)>i)
{
for(int j = i*i; j < n; j += i) bitmap[j] = false;
}
}
delete []bitmap;
return res;
}
#include <chrono>
#include <iostream>
using namespace std;
void test(const char* description, int (*fn)(int))
{
using clock = std::chrono::steady_clock;
using ms = std::chrono::milliseconds;
auto start = clock::now();
int a;
for(int i=0; i<9; ++i)
a = countPrimes_vector(8000000);
auto end = clock::now();
auto diff = std::chrono::duration_cast<ms>(end - start);
std::cout << "time for " << description << " = " << diff.count() << "ms\n";
}
int main()
{
test("carray", countPrimes_carray);
test("vector", countPrimes_vector);
}
time for carray = 2251ms
time for vector = 2254ms
虽然在某些运行中,carray慢了1-2毫秒。同样,共享资源上的样本不足。
---编辑---
在您的主要评论中,您要问&#34;为什么优化会产生影响&#34;。
std::vector<bool> v = { 1, 2, 3 };
bool b[] = { 1, 2, 3 };
我们有3个元素的两个&#34;数组,所以请考虑以下内容:
v[10]; // illegal!
b[10]; // illegal!
STL的调试版本通常可以在运行时捕获这个(并且在某些情况下,编译时)。数组访问可能只会导致错误的数据或崩溃。
此外,STL是使用许多小成员函数调用实现的,例如size()
,因为vector
是一个类,[]
实际上是通过函数调用(operator[]
来实现的。{ {1}})。
编译器可以消除其中许多,但这种优化。如果你没有优化,那么
std::vector<int> v;
v[10];
做了类似的事情:
int* data() { return M_.data_; }
v.operator[](size_t idx = 10) {
if (idx >= this->size()) {
raise exception("invalid [] access");
}
return *(data() + idx);
}
即使数据是&#34; inlinable&#34;函数,为了使调试更容易,未经优化的代码将其保留为此。当您使用优化进行构建时,编译器会认识到这些函数的实现是如此微不足道,它可以将它们的实现替换为调用站点,并且它很快就会将上述所有内容简化为更像数组访问的操作。
例如,在上述情况下,可以先将operator[]
减少为
v.operator[](size_t idx = 10) {
if (idx >= this->size()) {
raise exception("invalid [] access");
}
return *(M_.data_ + idx);
}
由于没有调试的编译可能会删除边界检查,因此它变为
v.operator[](size_t idx = 10) {
return *(M_.data_ + idx);
}
所以现在内联可以减少
x = v[1];
到
x = *(v.M_.data_ + 1); // comparable to v.M_.data_[1];
是一个很小的惩罚。 c数组涉及内存中的数据块和一个适合指向块的寄存器的局部变量,你的引用直接相对于:
但是,使用矢量,你有一个矢量对象,它是一个指向数据的指针,一个大小和一个容量变量:
vector<T> // pseudo code
{
T* ptr;
size_t size;
size_t capacity;
}
如果你在计算机器指令,矢量将有3个变量来初始化和管理。
写作时
x = v[1];
鉴于上面的向量近似,你要说的是:
T* ptr = v.data();
x = ptr[1];
但是编译器在构建优化时通常足够聪明,可以识别它可以在循环之前执行第一行,但这往往会花费一个寄存器。
T* ptr = v.data(); // in debug, function call, otherwise inlined.
for ... {
x = ptr[1];
}
因此,您可能每次迭代测试功能或在现代处理器上查看少量机器指令,可能需要几纳秒或两秒的额外停机时间。