[英]Is C++ std::vector slower than C99 variable length arrays?
我已經看到了一些有關此的討論。 許多人說他們應該保持相同的速度。 但是我自己做了一些測試。 在我看來,使用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
除了運行它們的析構函數和釋放內存所花費的時間外,
您沒有測量與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
內部只有一個數組,因此通過獲取第一個元素的地址並對該數組執行所有計算,您不會對vector做任何操作。 完全沒有意義的測試。
如果您測試a[i] = b[i] * c[i]
,則將顯示使用向量時是否存在差異。
目標代碼之間的細微差異可能是由於以下事實:當這些地址來自矢量的地址時,編譯器不夠聰明,無法告訴&a[0]
和&b[0]
和&c[0]
彼此互不別名。起始指針,而對於堆棧上聲明的數組,它可以告訴我們。
不管您的測量是沒有意義的,您都可以通過以下方式回答您的問題:“是的,但這並不重要,而且無論如何也無法進行比較”。
vector
在大多數情況下確實確實是“慢”的,但這並不重要,原因是vector
和C樣式數組是完全不同的事情,它們執行不同的操作。
數組分配一次並永久保持相同大小(直到釋放它)。 該分配可以在堆棧上進行(但不需要這樣做)。 另一方面, vector
必須必須在堆上分配其存儲空間,這不是一個自由操作(尤其是與在堆棧上分配時自動分配內存相比,釋放內存的開銷相當大-通常情況下是免費的並在最壞的情況下增加一個寄存器的值)。
數組中沒有其他“智能”。 沒有安全網。 推入更多元素而不適合,您將擁有UB。
vector
管理內部元素的存儲並提供可變大小,並在您按入更多元素時按需重新分配和復制元素,所有這些操作透明地進行,甚至不知道它會發生。 除非您作弊和過度分配,或者實現了vector
在幕后為您所做的相同工作,否則使用C樣式的數組根本不可能做到這一點。 在那一點上,數組變得和vector
一樣慢(並且可能更慢,除非您是一個非常有經驗的程序員)。
就此而言,以一種有意義的方式對兩者進行比較根本是不可能的,它們做的是完全不同的事情。 只有通過巧合(好吧,不是巧合,真的……但您明白我的意思),您可以將對象存儲在兩個對象中,並使用operator[]
訪問。
分配/重新分配很少發生(並且會攤銷,並且在大多數情況下可以通過適當使用reserve
來避免),因此盡管它們確實使vector
“變慢”,但在正常情況下它們對整體性能幾乎沒有影響。 只有分配和釋放數千個vector
和/或以非常錯誤的方式使用vector
才真正重要。
“ 99.9%重要”部分(例如,以給定索引訪問隨機元素)的vector
速度與C樣式數組的速度完全相同 。 當然,在大多數vector
實現都進行附加邊界檢查的調試版本中。 但這又是有用的額外功能,您當然需要付費。 在發布版本中,開銷消失了。
除非您的計算機比我的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;
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.