(是的,我知道有一個question幾乎相同的標題,但得到的答覆是不能令人滿意的,見下文)的性能,因爲在標準容器元素的默認初始化降解
編輯抱歉,原來的問題沒有使用編譯器優化。現在這個問題已經解決了,但爲了避免微不足道的優化,並且更接近我的實際使用案例,測試已經被分成了兩個編譯單元。
事實上,std::vector<>
的構造函數具有線性複雜度,這對於性能關鍵型應用程序來說是一件令人討厭的事情。當時間顯著量是通過構造x
浪費考慮這個簡單的代碼
// compilation unit 1:
void set_v0(type*x, size_t n)
{
for(size_t i=0; i<n; ++i)
x[i] = simple_function(i);
}
// compilation unit 2:
std::vector<type> x(n); // default initialisation is wasteful
set_v0(x.data(),n); // over-writes initial values
。解決這個問題的傳統方式,通過this question的探索,似乎僅僅保留的存儲和使用push_back()
填寫數據:
// compilation unit 1:
void set_v1(std::vector<type>&x, size_t n)
{
x.reserve(n);
for(size_t i=0; i<n; ++i)
x.push_back(simple_function(i));
}
// compilation unit 2:
std::vector<type> x(); x.reserve(n); // no initialisation
set_v1(x,n); // using push_back()
然而,我的評論所指示的,push_back()
是固有的緩慢,使得第二種方法比第一種用於充分簡單地constructible對象實際上較慢,如size_t
S,用於
simple_function = [](size_t i) { return i; };
我得到以下定時(使用gcc 4.8與-O3時;鐺3.2產生〜10%的SL奧爾代碼)
timing vector::vector(n) + set_v0();
n=10000 time: 3.9e-05 sec
n=100000 time: 0.00037 sec
n=1000000 time: 0.003678 sec
n=10000000 time: 0.03565 sec
n=100000000 time: 0.373275 sec
timing vector::vector() + vector::reserve(n) + set_v1();
n=10000 time: 1.9e-05 sec
n=100000 time: 0.00018 sec
n=1000000 time: 0.00177 sec
n=10000000 time: 0.020829 sec
n=100000000 time: 0.435393 sec
的加速實際上是可能的,如果能的Elid元素的默認構造可以通過下面的欺騙版本
// compilation unit 2
std::vector<type> x; x.reserve(n); // no initialisation
set_v0(x,n); // error: write beyond end of vector
// note: vector::size() == 0
當我們估計
timing vector::vector + vector::reserve(n) + set_v0(); (CHEATING)
n=10000 time: 8e-06 sec
n=100000 time: 7.2e-05 sec
n=1000000 time: 0.000776 sec
n=10000000 time: 0.01119 sec
n=100000000 time: 0.298024 sec
所以,我的第一個問題:是否有任何合法的方式來使用標準庫容器,這將給予後者的時間?或者我必須訴諸自己管理記憶?
現在我真正想要的就是用多線程來填充容器。幼稚代碼(在本例中爲簡單起見使用OpenMP,排除鐺的時刻)
// compilation unit 1
void set_v0(type*x, size_t n)
{
#pragma omp for // only difference to set_v0() from above
for(size_t i=0; i<n; ++i)
x[i] = simple_function(i);
}
// compilation unit 2:
std::vector<type> x(n); // default initialisation not mutli-threaded
#pragma omp parallel
set_v0(x,n); // over-writes initial values in parallel
現在的事實,所有元素的默認初始化是沒有多線程的,從而導致潛在的嚴重性能受損降解。這裏是(有4個核,8個超線程用我的MacBook的英特爾酷睿i7芯片)爲set_omp_v0()
時序和等效作弊方法:
timing std::vector::vector(n) + omp parallel set_v0()
n=10000 time: 0.000389 sec
n=100000 time: 0.000226 sec
n=1000000 time: 0.001406 sec
n=10000000 time: 0.019833 sec
n=100000000 time: 0.35531 sec
timing vector::vector + vector::reserve(n) + omp parallel set_v0(); (CHEATING)
n=10000 time: 0.000222 sec
n=100000 time: 0.000243 sec
n=1000000 time: 0.000793 sec
n=10000000 time: 0.008952 sec
n=100000000 time: 0.089619 sec
注意,作弊版本〜3.3倍,比串行作弊版本速度更快,大約如預期的那樣,但標準版本不是。
所以,我的第二個問題:是否有任何合法的方式來使用標準庫容器,這將在多線程情況下給後者定時?
PS。我找到了,其中std::vector
通過提供uninitialized_allocator
來避免默認初始化。 這不再符合標準,但對我的測試用例非常有用(請參閱我自己的答案,詳見this question)。
您是否可以使用emplace_back而不是push_back來構造「in-place」對象,從而保存默認構造對象的構造? – jcoder 2013-04-11 15:22:36
'emplace_back()'沒有區別。這裏的成本並不是構造元素(在我的例子中是一個微不足道的'size_t'),而是調整向量數據成員(儘管這應該只是一個'++指針')。 – Walter 2013-04-11 15:28:01
如果這是問題,'set_v0'應該很快,不是嗎?另外,你的默認標記爲「noexcept」嗎? – 2013-04-11 15:30:26