我已经看过一些关于此事的讨论。许多人说他们应该是同样的速度。 但我自己做了一些测试。在我看来,使用std :: vector的代码比使用数组的代码慢。但我不太明白为什么......我使用了以下简单的代码。
#include <cstdlib>
#include <vector>
#include <iostream>
#include <string>
#include <boost/date_time/posix_time/ptime.hpp>
#include <boost/date_time/microsec_time_clock.hpp>
class TestTimer
{
public:
TestTimer(const std::string & name) : name(name),
start(boost::date_time::microsec_clock<boost::posix_time::ptime>::local_time())
{}
~TestTimer()
{
using namespace std;
using namespace boost;
posix_time::ptime now(date_time::microsec_clock<posix_time::ptime>::local_time());
posix_time::time_duration d = now - start;
cout << name << " completed in " << d.total_milliseconds() / 1000.0 <<
" seconds" << endl;
}
private:
std::string name;
boost::posix_time::ptime start;
};
using namespace std;
int main(int argc, char** argv)
{
// timing for vector calculations
{
int n = 100000;
std::vector<double> a(n),b(n),c(n);
double* aa = &a[0];
double* bb = &b[0];
double* cc = &c[0];
TestTimer t("vector");
for ( long int j = 0; j < 1000; j ++ )
for ( int i = 0; i < n; i ++ )
aa[i]=bb[i]*cc[i];
}
// timing for array calculations
{
int n = 100000;
double a[n],b[n],c[n];
double* aa = &a[0];
double* bb = &b[0];
double* cc = &c[0];
TestTimer t("array");
for ( long int j = 0; j < 1000; j ++ )
for ( int i = 0; i < n; i ++ )
aa[i]=bb[i]*cc[i];
}
}
我使用-O0或-O3编译并运行icpc代码(g ++给出了非常相似的结果。我重复了几次,结果是一样的。):
icpc test.C -o test.x -O3 -Fa -g
./test.x
vector completed in 0.06 seconds
array completed in 0.03 seconds
icpc test.C -o test.x -O0 -Fa -g
./test.x
vector completed in 0.269 seconds
array completed in 0.279 seconds
看起来像-O3,使用数组计算比使用向量快2倍。我查看了汇编代码(对于循环中的部分)。对于使用-O0编译的汇编代码,它们对于矢量和数组看起来相同。但是当用-O3编译时,它们看起来完全不同。 (见下文) 我有点理解代码试图将数据从内存移动到寄存器(使用movapsx),执行乘法(mulpdx)并将数据移回(moapsx)。但是movsdq,movhpdq和movapsx有什么不同?为什么编译器会为数组和向量生成不同的代码?
汇编代码(循环部分)与-O3使用向量:
movsdq (%rbx,%r13,8), %xmm0
movsdq 0x10(%rbx,%r13,8), %xmm1
movsdq 0x20(%rbx,%r13,8), %xmm2
movsdq 0x30(%rbx,%r13,8), %xmm3
movhpdq 0x8(%rbx,%r13,8), %xmm0
movhpdq 0x18(%rbx,%r13,8), %xmm1
movhpdq 0x28(%rbx,%r13,8), %xmm2
movhpdq 0x38(%rbx,%r13,8), %xmm3
mulpdx (%r9,%r13,8), %xmm0
mulpdx 0x10(%r9,%r13,8), %xmm1
mulpdx 0x20(%r9,%r13,8), %xmm2
mulpdx 0x30(%r9,%r13,8), %xmm3
movapsx %xmm0, (%r15,%r13,8)
movapsx %xmm1, 0x10(%r15,%r13,8)
movapsx %xmm2, 0x20(%r15,%r13,8)
movapsx %xmm3, 0x30(%r15,%r13,8)
使用数组的-O3汇编代码(循环部分):
movapsx (%rbx,%rcx,8), %xmm0
movapsx 0x10(%rbx,%rcx,8), %xmm1
movapsx 0x20(%rbx,%rcx,8), %xmm2
movapsx 0x30(%rbx,%rcx,8), %xmm3
mulpdx (%rsi,%rcx,8), %xmm0
mulpdx 0x10(%rsi,%rcx,8), %xmm1
mulpdx 0x20(%rsi,%rcx,8), %xmm2
mulpdx 0x30(%rsi,%rcx,8), %xmm3
movapsx %xmm0, (%r13,%rcx,8)
movapsx %xmm1, 0x10(%r13,%rcx,8)
movapsx %xmm2, 0x20(%r13,%rcx,8)
movapsx %xmm3, 0x30(%r13,%rcx,8)
修改 我已更新代码以测试更多案例:
#include <cstdlib>
#include <vector>
#include <iostream>
#include <string>
#include <boost/date_time/posix_time/ptime.hpp>
#include <boost/date_time/microsec_time_clock.hpp>
class TestTimer
{
public:
TestTimer(const std::string & name) : name(name),
start(boost::date_time::microsec_clock<boost::posix_time::ptime>::local_time())
{}
~TestTimer()
{
using namespace std;
using namespace boost;
posix_time::ptime now(date_time::microsec_clock<posix_time::ptime>::local_time());
posix_time::time_duration d = now - start;
cout << name << " completed in " << d.total_milliseconds() / 1000.0 <<
" seconds" << endl;
}
private:
std::string name;
boost::posix_time::ptime start;
};
using namespace std;
int main(int argc, char** argv)
{
int n = 100;
int N = 10000000;
// timing for vector calculations
{
std::vector<double> a(n),b(n),c(n);
double* aa = &a[0];
double* bb = &b[0];
double* cc = &c[0];
{
TestTimer t("vector1");
for ( long int j = 0; j < N; j ++ )
for ( int i = 0; i < n; i ++ )
aa[i]=bb[i]*cc[i];
}
}
// timing for vector calculations
{
std::vector<double> a(n),b(n),c(n);
double* aa = &a[0];
double* bb = &b[0];
double* cc = &c[0];
{
TestTimer t("vector2");
for ( long int j = 0; j < N; j ++ )
for ( int i = 0; i < n; i ++ )
a[i]=b[i]*c[i];
}
}
// timing for array calculations
{
double a[n],b[n],c[n];
double* aa = &a[0];
double* bb = &b[0];
double* cc = &c[0];
{
TestTimer t("array");
for ( long int j = 0; j < N; j ++ )
for ( int i = 0; i < n; i ++ )
aa[i]=bb[i]*cc[i];
}
}
// timing for malloc calculations
{
double *a,*b,*c;
a=(double*)malloc(sizeof(double)*n);
b=(double*)malloc(sizeof(double)*n);
c=(double*)malloc(sizeof(double)*n);
double* aa = &a[0];
double* bb = &b[0];
double* cc = &c[0];
{
TestTimer t("malloc");
for ( long int j = 0; j < N; j ++ )
for ( int i = 0; i < n; i ++ )
aa[i]=bb[i]*cc[i];
}
}
// timing for new pointer calculations
{
double *a,*b,*c;
a=new double[n];
b=new double[n];
c=new double[n];
double* aa = &a[0];
double* bb = &b[0];
double* cc = &c[0];
{
TestTimer t("new pointer");
for ( long int j = 0; j < N; j ++ )
for ( int i = 0; i < n; i ++ )
aa[i]=bb[i]*cc[i];
}
}
}
对于n = 100,N = 10000000我得到:
g++ test.C -o test.x -O3 -Fa -g
./test.x
vector1 completed in 0.487 seconds
vector2 completed in 0.504 seconds
array completed in 1.624 seconds
malloc completed in 0.409 seconds
new pointer completed in 0.502 seconds
icpc test.C -o test.x -O3 -Fa -g
./test.x
vector1 completed in 0.318 seconds
vector2 completed in 0.319 seconds
array completed in 0.216 seconds
malloc completed in 0.295 seconds
new pointer completed in 0.289 seconds
对于n = 100000,N = 10000,我得到:
g++ test.C -o test.x -O3 -Fa -g
./test.x
vector1 completed in 0.699 seconds
vector2 completed in 0.648 seconds
array completed in 0.397 seconds
malloc completed in 0.428 seconds
new pointer completed in 0.464 seconds
icpc test.C -o test.x -O3 -Fa -g
./test.x
vector1 completed in 0.632 seconds
vector2 completed in 0.616 seconds
array completed in 0.308 seconds
malloc completed in 0.357 seconds
new pointer completed in 0.322 seconds
答案 0 :(得分:3)
除了运行析构函数和释放内存所花费的时间之外,你还没有测量与<{1}} 相关的任何,这不是必须的完成数组。
尝试将计时器放入新范围,因此在销毁向量之前停止计时,您应该看到非常相似的时间:
std::vector
原因是{
TestTimer t("vector");
for ( long int j = 0; j < 1000; j ++ )
for ( int i = 0; i < n; i ++ )
aa[i]=bb[i]*cc[i];
}
里面只有一个数组,所以通过获取第一个元素的地址并对该数组执行所有计算,你没有对向量做任何事情。完全毫无意义的测试。
如果您测试了std::vector
将显示使用向量时是否存在差异。
目标代码的微小差异可能是因为编译器不够聪明,无法告诉a[i] = b[i] * c[i]
和&a[0]
以及&b[0]
不要当这些地址来自向量的起始指针时,它们会互为别名,而它可以告诉堆栈中声明的数组。
答案 1 :(得分:0)
无论你的测量是毫无意义的,人们都可以用以下方式回答你的问题:&#34;是的,但没关系,并且它无论如何也无法比较&#34;。
vector
确实&#34;更慢&#34;大部分时间,但它几乎不重要,原因是因为vector
和C风格的数组完全不同,它们会做不同的事情。
一个数组被分配一次并永远保持相同的大小(直到你释放它)。这种分配可以在堆栈上发生(但不需要)。另一方面,{em>必须必须在堆上分配它的存储,这不是一个自由操作(特别是释放内存相比,在堆栈上分配时是自动的 - 在通常的情况下它是免费的,在最坏的情况下需要一个寄存器增量。) 没有额外的&#34;智能&#34;内置到数组中。没有安全网。推出比适合的更多的元素,你有UB。
vector
管理中的元素的存储,提供可变大小的和,当您推送更多元素时,根据需要重新分配和复制元素,所有这些都是透明的,没有您即使知道它发生了。除非你作弊和过度分配,或者你实现vector
幕后为你做的同样的工作,否则这些都不可能用C风格的数组。此时你的数组变得和vector
一样慢(而且很可能更慢,除非你是一个非常有经验的程序员)。
就目前而言,比较两者并非完全不可能有意义,他们完全不同。只有巧合(好吧,不是巧合,真的......但是你得到了我),你可以在两个文件中存储对象并使用vector
访问它们。
分配/重新分配很少发生(并且已经摊销,并且在大多数情况下可以通过正确使用operator[]
来避免),所以尽管它们会使reserve
&#34;更慢&#34;在正常情况下,它们对整体性能几乎无关紧要。只有你分配和释放数以千计的vector
和/或如果你以极其错误的方式使用vector
,这才真正重要。
&#34; 99.9%的重要&#34;部件,例如,访问给定索引处的随机元素与vector
完全的速度与C风格的数组相同。当然,在调试版本中,大多数vector
实现都会执行额外的边界检查。但这又是一个有用的额外功能,当然你必须付出代价。在发布版本中,开销消失了。
答案 2 :(得分:0)
除非您的机器比我的AMD Phenom II快100倍,否则我怀疑您的优化代码已完全消除了这两个循环。因为某种原因,我正在使用的clang ++(来自几周前的3.6)为VLA做了这个,但不是为了向量。不完全确定原因。
如果我添加代码实际使用aa
中的计算值,那么优化编译的结果几乎相同(并且在使用C ++向量时测量非优化代码的性能不正确,除非您实际打算提供您的生产代码,没有优化!)。通过优化(-O3),结果几乎相同:
vector completed in 0.286 seconds
x = 9.99985e+14
array completed in 0.284 seconds
x = 9.99985e+14
我运行了几次,第二个(数组)变体总是快1-3毫秒,所以约占总时间的1%。
这是我的代码:
#include <cstdlib>
#include <vector>
#include <iostream>
#include <string>
#include <boost/date_time/posix_time/ptime.hpp>
#include <boost/date_time/microsec_time_clock.hpp>
class TestTimer
{
public:
TestTimer(const std::string & name) : name(name),
start(boost::date_time::microsec_clock<boost::posix_time::ptime>::local_time())
{}
~TestTimer()
{
using namespace std;
using namespace boost;
posix_time::ptime now(date_time::microsec_clock<posix_time::ptime>::local_time());
posix_time::time_duration d = now - start;
cout << name << " completed in " << d.total_milliseconds() / 1000.0 <<
" seconds" << endl;
}
private:
std::string name;
boost::posix_time::ptime start;
};
using namespace std;
int main(int argc, char** argv)
{
double x = 0;
// timing for vector calculations
{
int n = 100000;
std::vector<double> a(n),b(n),c(n);
double* aa = &a[0];
double* bb = &b[0];
double* cc = &c[0];
for(int i = 0; i < n; i++)
{
bb[i] = i * 2;
cc[i] = i * 1.5;
}
TestTimer t("vector");
for ( long int j = 0; j < 1000; j ++ )
for ( int i = 0; i < n; i ++ )
aa[i]=bb[i]*cc[i];
for(auto v : a)
{
x += v;
}
}
cout << "x = " << x << endl;
// timing for array calculations
{
int n = 100000;
double a[n],b[n],c[n];
double* aa = &a[0];
double* bb = &b[0];
double* cc = &c[0];
for(int i = 0; i < n; i++)
{
bb[i] = i * 2;
cc[i] = i * 1.5;
}
TestTimer t("array");
for ( long int j = 0; j < 1000; j ++ )
for ( int i = 0; i < n; i ++ )
aa[i]=bb[i]*cc[i];
x = 0;
for(auto v : a)
{
x += v;
}
}
cout << "x = " << x << endl;
}