C++是否该使用vector作为返回值

文/fasiondog

一直对函数中是否应该直接返回vector有疑惑,今日总算动手测试了一番。大体是设计一个对象,其内存有一个数组,改变这个数组的存储方式,测试其赋值构造函数+循环取数的性能。其中,顺便测试了一下C++11移动语义的效率。大致分了一下情景(代码间后):

  • A:使用vector<int>保存内部数据,深拷贝,不使用reserve预先调整vector空间
  • A1:使用vector<int>保存内部数据,深拷贝,使用reserve预先调整vector空间
  • B:使用shared_ptr<vector<int> >保存内部数据,浅拷贝
  • C:使用vector<int> * 保存内部数据,深拷贝
  • D:使用vector<int> * 保存内部数据,深拷贝,实现C++11移动语义
  • E:使用int *保存内部数据,深拷贝,循环赋值
  • E1:使用int *保存内部数据,深拷贝,循环赋值,C++移动语义
  • E2:使用int *保存内部数据,深拷贝,memcpy
  • E3:使用int *保存内部数据,深拷贝,memcpy,C++移动语义

结果发现使用shared_ptr并非想象中的能加快速度(赋值构造时不需要赋值vector)。这种情况下shared_ptr的好处就只是节省内存了。通过结果比较大致的结论如下:

  • 如果预先知道数据的大小,使用vector中的reserve事先调整vector的容量,速度提升非常明显
  • 直接使用vector(配合reserve)并不比使用shared_ptr慢,所以在不需要考虑节省内存的情况下(shared_ptr是浅拷贝),直接使用vector,即使做为函数返回值,速度也是相当的。(甚至,在MSVC中,shared_ptr更慢!)
  • 在不考虑线程安全的情况下,追求极限速度,还是直接使用原始指针吧

具体测试的结果如下,其中“未优化”是指关闭编译器优化,“编译优化”中msvc11使用“/O2”、g++(4.8)使用“-O3”编译选项:

no_optimize optimize

具体数据:

未优化:

类别 MSVC11 G++
A 13.264 11.093
A1 12.471 10.873
B 15.138 12.482
C 12.867 11.412
D 12.862 11.261
E 6.485 5.438
E1 3.907 3.347
E2 4.017 3.451
E3 3.767 3.348

编译优化:

类别 MSVC11 /O2 G++ -O3
A 2.491 1.536
A1 1.711 0.9417
B 1.877 0.9137
C 2.018 1.2394
D 1.893 0.9334
E 0.96 0.6702
E1 0.572 0.36
E2 0.715 0.4476
E3 0.561 0.359

测试代码如下:
博客主题和代码加亮插件配合有问题,用链接里的看吧
https://github.com/fasiondog/temp/blob/master/test-1/test.cpp

[cpp]
//使用vector保存数据,深拷贝,不使用reserve
class A {
public:
A(int n) {
for (int i = 0; i < n; ++i) {
_list.push_back(i);
}
}

A(const A& x) {//: _list(x._list) {
//std::cout << “A(const A& x)” << std::endl;
_list = x._list;
}

virtual ~A() {}

size_t size() const { return _list.size(); }

int operator[](int i) const { return _list[i];}

private:
std::vector _list;
};

//使用vector保存数据,深拷贝,使用reserve
class A1 {
public:
A1(int n) {
_list.reserve(n);
for (int i = 0; i < n; ++i) {
_list.push_back(i);
}
}

A1(const A1& x) {// : _list(x._list) {
//std::cout << “A1(const A1& x)” << std::endl;
_list = x._list;
}

virtual ~A1() {}

size_t size() const { return _list.size(); }

int operator[](int i) const { return _list[i];}

private:
std::vector _list;
};

//使用shared_ptr<vector >保存内部数据,浅拷贝,reserve
class B {
public:
B(int n) {
_plist = std::shared_ptr<std::vector >(new std::vector);
_plist->reserve(n);
for (int i = 0; i < n; ++i) {
_plist->push_back(i);
}
}

B(const B& x) { //: _plist(x._plist) {
//std::cout << “B(const B& x)” << std::endl;
_plist = x._plist;
}

virtual ~B() {}

size_t size() const { return _plist->size(); }

int operator[](int i) const { return (*_plist)[i];}

private:
std::shared_ptr<std::vector > _plist;
};

//使用vector * 保存内部数据,深拷贝,reserve
class C {
public:
C(int n) {
_plist = new std::vector;
_plist->reserve(n);
for (int i = 0; i < n; ++i) {
_plist->push_back(i);
}
}

C(const C& x) {
//std::cout << “C(const C& x)” << std::endl;
_plist = new std::vector;
(*_plist) = (*x._plist);
/*_plist->reserve(x.size());
size_t total = x.size();
for (int i = 0; i < total; ++i) {
_plist->push_back(x[i]);
}*/
}

virtual ~C() { delete _plist; }

size_t size() const { return _plist->size(); }

int operator[](int i) const { return (*_plist)[i];}

private:
std::vector *_plist;
};

//使用vector * 保存内部数据,深拷贝,reserve,实现C++11移动语义
class D {
public:
D(int n) {
_plist = new std::vector;
_plist->reserve(n);
for (int i = 0; i < n; ++i) {
_plist->push_back(i);
}
}

D(const D& x) {
std::cout << “D(const D& x)” << std::endl;
_plist = new std::vector;
(*_plist) = (*x._plist);
/*_plist->reserve(x.size());
for (int i = 0; i < x.size(); ++i) {
_plist->push_back(x[i]);
}*/
}

D(D&& x) { //: _plist(x._plist) {
//std::cout << “D(D&& x)” << std::endl;
_plist = x._plist;
x._plist = 0;
}

virtual ~D() { delete _plist; }

size_t size() const { return _plist->size(); }

int operator[](int i) const { return (*_plist)[i];}

private:
std::vector *_plist;
};

//使用int *保证内部数据,深拷贝
class E {
public:
E(int n):_size(n) {
_plist = new int[n];
for (int i = 0; i < n; ++i) {
_plist[i] = i;
}
}

E(const E& x):_size(x._size) {
//std::cout << “E(const E& x)” << std::endl;
_plist = new int[_size];
std::memcpy(_plist, x._plist, x._size * sizeof(int));
/*for (int i = 0; i < x.size(); ++i) {
_plist[i] = x[i];
}*/
}

#if 1
E(E&& x): _size(x._size), _plist(x._plist) {
//std::cout << “E(E&& x)” << std::endl;
x._plist = 0;
}
#endif

virtual ~E() { delete[] _plist; }

size_t size() const { return _size; }

int operator[](int i) const { return _plist[i];}

private:
size_t _size;
int *_plist;
};

template
T func(int n) {
T result(n);
if (n <= 1) { //此处只用<,会被msvc优化掉
//std::cout << “T Func” << std::endl;
return T(1);
}
return result;
}

///编译时,需注意编译器优化NRO
int main() {
unsigned long long t1=0, t2=0;
int max = 50000;

for (int i = 1; i < max; ++i) {
//A a = func(i);
//A1 a = func(i);
//B a = func(i);
//C a = func(i);
//D a = func(i);
E a = func(i);
for (int j = 1; j < a.size(); ++j) {
t1 += a[j];
}
//t1 += a.size();
}

std::cout << t1 << std::endl;
}
[/cpp]

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注