3

我的系统:

系统规格:Intel core2duo E4500 3700g 内存 L2 cache 2M x64 fedora 17

我如何测量 flops/mflops

好吧,我使用 papi 库(读取硬件性能计数器)来测量我的代码的 flops 和 mflops。它返回实时处理时间、触发器和最后等于 MFLOPS 的 flops/处理时间。库使用硬件计数器来计算浮点数指令或浮点运算和总周期以获得包含触发器和 MFLOPS 的最终结果。

我的计算内核

我使用了三个循环矩阵矩阵乘法(方阵)和三个嵌套循环,它们在内循环中对一维数组进行一些操作。

第一个内核 MM

    float a[size][size];
    float b[size][size];
    float c[size][size];

 start_calculate_MFlops();

for (int i = 0; i < size; ++i) {
        for (int j = 0; j < size; ++j) {
            for (int k = 0; k < size; **k+=1**) {
                *c[i][j]=c[i][j]+a[i][k] * b[k][j];*
                     }
            }
 }
  stop_calculate_MFlops();

具有一维数组的第二个内核

    float d[size];
    float e[size];
    float f[size];
    float g[size];
    float r = 3.6541;

 start_calculate_MFlops();

for (int i = 0; i < size; ++i) {
    for (int j = 0; j < size; ++j) {
        for (int k = 0; k < size; ++k) {
            d[k]=d[k]+e[k]+f[k]+g[k]+r;
        }
    }
}    

stop_calculate_MFlops();

我对人字拖的了解

矩阵矩阵乘法 (MM) 在其内部循环中执行 2 次运算(此处为浮点运算),并且由于有 3 个循环针对大小 X 进行迭代,因此理论上我们对于 MM 有 2*n^3 的总触发器。

在第二个内核中,我们有 3 个循环,在最内层的循环中,我们有 1d 数组来进行一些计算。在这个循环中有 4 个浮点运算。因此理论上我们总共有 4*n^3 次触发器

我知道我们上面计算的翻牌与真实机器中发生的翻牌并不完全相同。在真实机器中还有其他操作,如加载和存储,它们将加起来理论上的失败。

问题 ?:

  1. 当我在第二个内核中使用一维数组时,理论触发器是相同的或围绕我通过执行代码并测量它得到的触发器。实际上,当我使用一维数组时,触发器等于最内层循环中的操作数乘以 n^ 3 但是当我使用我的第一个使用 2d 数组的内核 MM 时,理论触发器是 2n^3 但是当我运行代码时,测量值比理论值高太多,大约是 4+(2 的最内层循环中的操作矩阵乘法)*n^3+=6n^3。我只用下面的代码更改了最内层循环中的矩阵乘法线:

    A[i][j]++;
    

    该代码在 3 个嵌套循环中的理论失败是 1 次操作 * n^3=n^3 当我再次运行代码时,结果比预期的要高,即 2+(最内层循环的 1 次操作)*n ^3=3*n^3

    大小为 512X512 的矩阵的示例结果:

    Real_time:1.718368 Proc_time:1.227672 总浮点数: 807,107,072 MFLOPS:657.429016

    Real_time:3.608078 Proc_time:3.042272 总浮点数: 807,024,448 MFLOPS:265.270355

    理论翻牌:2*512*512*512= 268,435,456

    实测翻牌 = 6*512^3 = 807,107,072

    3 嵌套循环中一维数组操作的示例结果

    Real_time:1.282257 Proc_time:1.155990 总触发器: 536,872,000 MFLOPS:464.426117

    理论翻牌: 4n^3 = 536,870,912

    实测翻牌: 4n^3=4*512^3+开销(其他操作?)= 536,872,000

我找不到上述行为的任何原因? 我的假设是真的吗?

希望能比之前的描述简单很多。

实际上,我的意思是通过执行代码来衡量真正的失败。

代码:

 void countFlops() {

    int size = 512;
    int itr = 20;
    float a[size][size];
    float b[size][size];
    float c[size][size];
/*  float d[size];
    float e[size];
    float f[size];
    float g[size];*/
        float r = 3.6541;

    float real_time, proc_time, mflops;
    long long flpops;
    float ireal_time, iproc_time, imflops;
    long long iflpops;
    int retval;

    for (int i = 0; i < size; ++i) {
        for (int j = 0; j < size; ++j) {
            a[j][j] = b[j][j] = c[j][j] = 1.0125;
        }
    }

/*  for (int i = 0; i < size; ++i) {
                d[i]=e[i]=f[i]=g[i]=10.235;
        }*/

    if ((retval = PAPI_flops(&ireal_time, &iproc_time, &iflpops, &imflops))
            < PAPI_OK) {
        printf("Could not initialise PAPI_flops \n");
        printf("Your platform may not support floating point operation event.\n");
        printf("retval: %d\n", retval);
        exit(1);
    }
    for (int i = 0; i < size; ++i) {
        for (int j = 0; j < size; ++j) {
            for (int k = 0; k < size; k+=16) {
                c[i][j]=c[i][j]+a[i][k] * b[k][j];
            }
        }
    }

/*  for (int i = 0; i < size; ++i) {
    for (int j = 0; j < size; ++j) {
        for (int k = 0; k < size; ++k) {
            d[k]=d[k]+e[k]+f[k]+g[k]+r;
        }
    }
    }*/

    if ((retval = PAPI_flops(&real_time, &proc_time, &flpops, &mflops))
            < PAPI_OK) {
        printf("retval: %d\n", retval);
        exit(1);
    }
    string flpops_tmp;
    flpops_tmp = output_formatted_string(flpops);
    printf(
            "calculation: Real_time: %f Proc_time: %f Total flpops: %s MFLOPS: %f\n",
            real_time, proc_time, flpops_tmp.c_str(), mflops);

}

谢谢你

4

1 回答 1

1

如果您需要计算操作的数量 - 您可以制作简单的类,它的作用类似于浮点值并收集统计信息。它将与内置类型互换。

现场演示

#include <boost/numeric/ublas/matrix.hpp>
#include <boost/operators.hpp>
#include <iostream>
#include <ostream>
#include <utility>
#include <cstddef>
#include <vector>

using namespace boost;
using namespace std;

class Statistic
{
    size_t ops = 0;
public:
    Statistic &increment()
    {
        ++ops;
        return *this;
    }
    size_t count() const
    {
        return ops;
    }
};

template<typename Domain>
class Profiled: field_operators<Profiled<Domain>>
{
    Domain value;
    static vector<Statistic> stat;
    void stat_increment()
    {
        stat.back().increment();
    }
public:
    struct StatisticScope
    {
        StatisticScope()
        {
            stat.emplace_back();
        }
        Statistic &current()
        {
            return stat.back();
        }
        ~StatisticScope()
        {
            stat.pop_back();
        }
    };
    template<typename ...Args>
    Profiled(Args&& ...args)
        : value{forward<Args>(args)...}
    {}
    Profiled& operator+=(const Profiled& x)
    {
        stat_increment();
        value+=x.value;
        return *this;
    }
    Profiled& operator-=(const Profiled& x)
    {
        stat_increment();
        value-=x.value;
        return *this;
    }
    Profiled& operator*=(const Profiled& x)
    {
        stat_increment();
        value*=x.value;
        return *this;
    }
    Profiled& operator/=(const Profiled& x)
    {
        stat_increment();
        value/=x.value;
        return *this;
    }
};
template<typename Domain>
vector<Statistic> Profiled<Domain>::stat{1};

int main()
{
    typedef Profiled<double> Float;
    {
        Float::StatisticScope s;
        Float x = 1.0, y = 2.0, res = 0.0;
        res = x+y*x+y;
        cout << s.current().count() << endl;
    }
    {
        using namespace numeric::ublas;
        Float::StatisticScope s;
        matrix<Float> x{10, 20},y{20,5},res{10,5};
        res = prod(x,y);
        cout << s.current().count() << endl;
    }
}

输出是:

3
2000

PS您的矩阵循环对缓存不友好,因此效率非常低

聚苯乙烯

int size = 512;
float a[size][size];

这不是合法的 C++ 代码。C++ 不支持VLA

于 2013-04-06T14:42:37.430 回答