簡體   English   中英

固定大小數組的STL算法的效率

[英]Efficiency of STL algorithms with fixed size arrays

總的來說,我認為任何算法的STL實現都至少和我能想到的效率一樣高(具有無錯誤的額外好處)。 但是,我開始懷疑STL在某些情況下對迭代器的關注是否有害。

假設我要計算兩個固定大小的數組的內積。 我的幼稚實現看起來像這樣:

std::array<double, 100000> v1;
std::array<double, 100000> v2;
//fill with arbitrary numbers

double sum = 0.0;
for (size_t i = 0; i < v1.size(); ++i) {
    sum += v1[i] * v2[i];
}

由於在編譯時已知迭代次數和內存布局,並且所有操作都可以直接映射到本機處理器指令,因此編譯器應該可以輕松地從中生成“最佳”機器代碼(循環展開,向量化/ FMA指令) ...)。

STL版本

double sum = std::inner_product(cbegin(v1), cend(v1), cbegin(v2), 0.0);

另一方面,即使添加了所有內聯函數,編譯器仍然必須推斷出它正在連續內存區域上以及該區域所在的位置上進行操作,即使所有內容都已內聯。 盡管原則上這當然是可能的,但我想知道典型的c ++編譯器是否會真正做到這一點。

因此,我的問題是 :您認為,自行實現對固定大小的數組進行操作的標准算法會帶來性能上的好處,還是STL版本總是優於手動實現?

按照建議,我做了一些測量,

  • 對於下面的代碼
  • 在發布模式下使用VS2013 for x64編譯
  • 在裝有i7-2640M的Win8.1機器上執行,

算法版本始終會慢20% (15.6-15.7s和12.9-13.1s)。 NREPS的相對差異在兩個數量級上也大致保持恆定。

所以我猜答案是: 使用標准庫算法會損害性能。

如果這是一個普遍的問題,或者它是特定於我的平台,編譯器和基准測試,那么仍然會很有趣。 歡迎您發布自己的結果或對基准發表評論。

#include <iostream>
#include <numeric>
#include <array>
#include <chrono>
#include <cstdlib>

#define USE_STD_ALGORITHM

using namespace std;
using namespace std::chrono;

static const size_t N = 10000000; //size of the arrays
static const size_t REPS = 1000; //number of repitions

array<double, N> a1;
array<double, N> a2;

int main(){
    srand(10);
    for (size_t i = 0; i < N; ++i) {
        a1[i] = static_cast<double>(rand())*0.01;
        a2[i] = static_cast<double>(rand())*0.01;
    }

    double res = 0.0;
    auto start=high_resolution_clock::now();
    for (size_t z = 0; z < REPS; z++) {     
        #ifdef USE_STD_ALGORITHM
            res = std::inner_product(a1.begin(), a1.end(), a2.begin(), res);        
        #else           
            for (size_t t = 0; t < N; ++t)  {
                res+= a1[t] * a2[t];
            }
        #endif        
    }
    auto end = high_resolution_clock::now();

    std::cout << res << "  "; // <-- necessary, so that loop isn't optimized away
    std::cout << duration_cast<milliseconds>(end - start).count() <<" ms"<< std::endl;

}
/* 
 * Update: Results (ubuntu 14.04 , haswell)
 * STL: algorithm
 * g++-4.8-2    -march=native -std=c++11 -O3 main.cpp               : 1.15287e+24  3551 ms
 * g++-4.8-2    -march=native -std=c++11 -ffast-math -O3 main.cpp   : 1.15287e+24  3567 ms
 * clang++-3.5  -march=native -std=c++11 -O3 main.cpp               : 1.15287e+24  9378 ms
 * clang++-3.5  -march=native -std=c++11 -ffast-math -O3 main.cpp   : 1.15287e+24  8505 ms
 *
 * loop:
 * g++-4.8-2    -march=native -std=c++11 -O3 main.cpp               : 1.15287e+24  3543 ms
 * g++-4.8-2    -march=native -std=c++11 -ffast-math -O3 main.cpp   : 1.15287e+24  3551 ms
 * clang++-3.5  -march=native -std=c++11 -O3 main.cpp               : 1.15287e+24  9613 ms
 * clang++-3.5  -march=native -std=c++11 -ffast-math -O3 main.cpp   : 1.15287e+24  8642 ms
 */  

編輯:
我在同一台計算機上的fedora 21 Virtual Box VM上使用O3std=c++11用g ++-4.9.2和clang ++-3.5進行了快速檢查,顯然那些編譯器沒有相同的問題(時間是兩個版本幾乎相同)。 但是,gcc的版本速度大約是clang的兩倍(7.5s對14s)。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM