c++ - 具有固定大小数组的 STL 算法的效率

标签 c++ arrays algorithm stl

一般来说,我假设任何算法的 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];
}

由于迭代次数和内存布局在编译期间是已知的,并且所有操作都可以直接映射到 native 处理器指令,因此编译器应该能够轻松地从中生成“最佳”机器代码(循环展开、向量化/FMA 指令...)。

STL版本

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

另一方面添加了一些额外的间接寻址,即使所有内容都是内联的,编译器仍然必须推断它正在处理一个连续的内存区域以及该区域所在的位置。虽然原则上这当然是可能的,但我想知道典型的 c++ 编译器是否真的会这样做。

所以我的问题是:您认为,我自己实现在固定大小数组上运行的标准算法是否有性能优势,或者 STL 版本是否总是优于手动实现?

最佳答案

按照建议我做了一些测量和

  • 下面的代码
  • 在 Release模式下使用 VS2013 为 x64 编译
  • 在配备 i7-2640M 的 Win8.1 机器上执行,

算法版本持续慢了大约 20%(15.6-15.7 秒对 12.9-13.1 秒)。 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.5 秒对 14 秒)。

关于c++ - 具有固定大小数组的 STL 算法的效率,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/22621920/

相关文章:

c++ - 处理数组访问时可能会生成不一致的代码

关于类设计异常处理的 C++ 帮助

c# - 为什么 C++ 读取字节结果与 C# 不同?

java - 使用最小数量的矩形覆盖 "Manhattan skyline"

java - 如何获得填字游戏的多个解决方案?

python - python 中的强数字

c++ - 如何将文本放入 OpenCV 中的边界框?

c++ - 是否可以使用自定义分配运算符来创建 STACK 对象?

ios - 通过 swift 重新排序字典数组

c++ - 在数组中存储类对象