对大矢量进行定时数值运算.如何进行公平的比较

Timing numerical operations on large vectors. How to draw a fair comparison?

本文关键字:何进行 运算 比较 定时      更新时间:2023-10-16

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

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

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

原谅代码中的宏;它们只是CCD_ 5语句和对CCD_。

提前谢谢。

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() << " msecn";
    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() << " msecn";
    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() << " msecn";
    PRINTV(valarray_times)
    PRINTME(result, aa)
    return 0;
}

C++输出:

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

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 57 57 56 57 55 58 58 90

结果:1.97392e+08

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

valarray_times=63 64 63 64 65 63 63 63 63 64 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输出:

平均时间=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还有其他障碍。

一次又一次地计算点积可能会受到内存限制。如果你想大致了解你所期望的速度差异,最好比较一下矩阵乘法之类的东西。点产品也很简单,你只需检查汇编代码就可以了解发生了什么;我鼓励你这样做。

你对valarrays的比较有点不公平;你做两个数组,然后把它们加在一起,然后求和。最好是计算这两个和并将它们相加。(我不知道如何避免使用valarray接口对整个阵列进行额外扫描。)