对大向量进行定时数值运算。如何进行公平的比较?

时间:2014-04-08 23:27:08

标签: c++ performance numerical

我正在计算一些简单的数值运算来决定如何(即,使用哪个库中的工具)我将实现计算密集型模拟。下面的代码使用(1)MTL4版本4.0.9486(即没有BLAS),(2)std::vectorstd::inner_product来计算向量的两个内积的总和, (3)std::valarray s。我选择了这种特定形式的玩具示例,因为它似乎是MTL4表达模板的理想基础。

将所有内容缩小到一个问题,是下面的比较公平还是(无意中)三种方法中的任何一种处于不利地位?我有点惊讶(2)比(1)快。当然,整体模拟是否会更快是一个不同的故事。

如果有人对任何可能揭示每种方法的优点或缺点的更彻底的测试有任何建议,我很想尝试一下。

请原谅代码中的宏;它们只是std::cout<<语句并调用<chrono>实用程序。

提前致谢。

C ++代码:

#include <iostream>
#include <valarray>
#include <vector>
#include <algorithm>
#include <boost/numeric/mtl/mtl.hpp>

int main(int argc, const char * argv[])
{
    /* DOT PRODUCTS */
    constexpr int trials{15};
    std::vector<double> mtl_times(trials, 0.0), stl_times(trials, 0.0), valarray_times(trials, 0.0);

    constexpr size_t sz{10000000};
    double val = M_PI;
    mtl::dense_vector<double> m(sz, val), n(sz, val), p(sz, val), q(sz, val);
    std::vector<double> y(sz, val), z(sz, val), v(sz, val), w(sz, val);
    std::valarray<double> b(val, sz), c(val, sz), d(val, sz), e(val, sz);

    double x{0.0}, k{0.0}, aa{0.0};
    auto t0 = NOW
    auto t1 = t0;

    for (int i = 0; i < trials; ++i) {

        // MTL4 vectors
        t0 = NOW // call now() from <chrono>
        k = dot(m, n) + dot(p, q);
        t1 = NOW
        mtl_times[i] = DURATIONm // duration cast of (t1-t0).count()

        // STL vectors
        t0 = NOW
        x = std::inner_product(y.begin(), y.end(), z.begin(), 0.0) + std::inner_product(v.begin(), v.end(), w.begin(), 0.0);
        t1 = NOW
        stl_times[i] = DURATIONm

        // valarrays
        t0 = NOW
        aa = (b*c + d*e).sum();
        t1 = NOW
        valarray_times[i] = DURATIONm

    }

    std::cout << "MTL4: average time for dot product = " << std::accumulate(mtl_times.begin(), mtl_times.end(), 0.0)/mtl_times.size() << " msec\n";
    PRINTV(mtl_times)
    PRINTME(result, k)
    std::cout << '\n';

    std::cout << "STL vectors + std::inner_product: average time for dot product = " << std::accumulate(stl_times.begin(), stl_times.end(), 0.0)/stl_times.size() << " msec\n";
    PRINTV(stl_times)
    PRINTME(result, x)
    std::cout << '\n';

    std::cout << "valarrays: average time for dot product = " << std::accumulate(valarray_times.begin(), valarray_times.end(), 0.0)/valarray_times.size() << " msec\n";
    PRINTV(valarray_times)
    PRINTME(result, aa)

    return 0;
}

C ++输出:

  

MTL4:点积的平均时间= 180.333毫秒

     

mtl_times =   177 175 174 174 175 178 176 185 184 174 175 179 175 216 188

     

结果:1​​.97392e + 08

     

STL向量+ std :: inner_product:点积的平均时间= 58.6毫秒

     

stl_times = 56 55 56 57 57 56 57 56 57 55 55 58 56 58 90

     

结果:1​​.97392e + 08

     

valarrays:点积的平均时间= 64.4毫秒

     

valarray_times = 63 64 63 64 65 63 63 63 64 63 63 63 64 64 77

     

结果:1​​.97392e + 08

为了记录,MatLab表现良好:

MatLab代码:

trials = 15;
times_ms = zeros(1, trials);

sz = 1e7;
val = pi;
x(sz) = val;
x(1:end-1) = val;
y(sz) = val;
y(1:end-1) = val;
v(sz) = val;
v(1:end-1) = val;
w(sz) = val;
w(1:end-1) = val;

z = 0;

for i = 1:trials

    tic
    z = x*y' + v*w';
    times_ms(i) = toc*1e3;

end

avg_time = sum(times_ms)/length(times_ms)
times_ms
z

MatLab输出:

  

avg_time = 56.0687毫秒

     

times_ms = 56.8919 57.2052 55.3179 55.5126 55.7660 55.3982 55.1044 55.4809 57.7229 56.1902 57.3888 56.5263 55.2830 55.4926 55.7501

     

z = 1.9739e + 08

这并不奇怪,因为内置操作已经过优化,但在模拟中使用MatLab还存在其他障碍。

1 个答案:

答案 0 :(得分:2)

反复计算点积可能会受内存限制。如果你想要粗略地理解你可以期待的速度差异,那么比较像矩阵乘法这样的东西会更好。 Dot产品也很简单,您只需检查汇编代码即可查看正在进行的操作;我鼓励你这样做。

你的比较对valarrays有点不公平;你制作了两个数组,然后将它们加在一起,然后取出它们的总和。计算两个总和并添加它们可能更好。 (我不知道如何使用valarray接口避免对整个阵列进行额外扫描。)