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

fasiondog · · 6,995次浏览 ·

一直对函数中是否应该直接返回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

//使用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<a>(i);
		//A1 a = func(i);
        //B a = func<b>(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;
}


发表评论

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

我不是机器人*