计算浮点指令

时间:2014-06-02 17:36:49

标签: c++ linux performance perf

我正在尝试计算其中一个程序中的浮点运算数量,我认为perf可能是我正在寻找的工具(还有其他选择吗?),但我无法将其限制为某个功能/代码块。让我们举个例子:

#include <complex>
#include <cstdlib>
#include <iostream>
#include <type_traits>

template <typename T>
typename std::enable_if<std::is_floating_point<T>::value, T>::type myrand()
{
        return static_cast <T> (std::rand()) / static_cast <T> (RAND_MAX);
}

template <typename T>
typename std::enable_if<!std::is_floating_point<T>::value, std::complex<typename T::value_type>>::type myrand()
{
        typedef typename T::value_type S;

        return std::complex<S>(
                static_cast <S> (std::rand()) / static_cast <S> (RAND_MAX),
                static_cast <S> (std::rand()) / static_cast <S> (RAND_MAX)
        );
}

int main()
{
    auto const a = myrand<Type>();
    auto const b = myrand<Type>();

    // count here
    auto const c = a * b;
    // stop counting here

    // prevent compiler from optimizing away c
    std::cout << c << "\n";

    return 0;
}

myrand()函数只返回一个随机数,如果类型T是复数则是随机复数。我没有将双打硬编码到程序中,因为它们会被编译器优化掉。

您可以使用bench.cpp编译文件(我们称之为c++ -std=c++0x -DType=double bench.cpp)。

现在我想计算浮点运算的数量,这可以在我的处理器(Nehalem架构,x86_64,其中浮点是用标量SSE完成)和事件r8010上完成(参见英特尔手册3B) ,第19.5节)。这可以通过

完成
perf stat -e r8010 ./a.out

并按预期工作;然而,它计算了uops的整体数量(是否有一个表格告诉movsd例如有多少uops?)和我只对乘法的数量感兴趣(参见上面的例子)。

如何做到这一点?

1 个答案:

答案 0 :(得分:2)

我终于找到了一种方法,虽然没有使用perf,而是使用相应的perf API。首先必须定义一个perf_event_open函数,它实际上是一个系统调用:

#include <cstdlib> // stdlib.h for C
#include <cstdio> // stdio.h for C
#include <cstring> // string.h for C
#include <unistd.h>
#include <sys/ioctl.h>
#include <linux/perf_event.h>
#include <asm/unistd.h>

long perf_event_open(
    perf_event_attr* hw_event,
    pid_t pid,
    int cpu,
    int group_fd,
    unsigned long flags
) {
    int ret = syscall(__NR_perf_event_open, hw_event, pid, cpu, group_fd, flags);
    return ret;
}

接下来,选择一个人想要计算的事件:

perf_event_attr attr;

// select what we want to count
std::memset(&attr, 0, sizeof(perf_event_attr));
attr.size = sizeof(perf_event_attr);
attr.type = PERF_TYPE_HARDWARE;
attr.config = PERF_COUNT_HW_INSTRUCTIONS;
attr.disabled = 1;
attr.exclude_kernel = 1; // do not count the instruction the kernel executes
attr.exclude_hv = 1;

// open a file descriptor
int fd = perf_event_open(&attr, 0, -1, -1, 0);

if (fd == -1)
{
    // handle error
}

在这种情况下,我想简单地计算指令的数量。通过用

替换相应的行,可以在我的处理器(Nehalem)上计算浮点指令
attr.type = PERF_TYPE_RAW;
attr.config = 0x8010; // Event Number = 10H, Umask Value = 80H

通过将类型设置为RAW,基本上可以计算处理器提供的每个事件;数字0x8010指定哪一个。 请注意,此数字与处理器高度相关!通过选择正确的小节,可以在英特尔手册3B第2部分第19章中找到正确的数字。

然后可以通过将代码包含在

中来测量代码
// reset and enable the counter
ioctl(fd, PERF_EVENT_IOC_RESET, 0);
ioctl(fd, PERF_EVENT_IOC_ENABLE, 0);

// perform computation that should be measured here

// disable and read out the counter
ioctl(fd, PERF_EVENT_IOC_DISABLE, 0);
long long count;
read(fd, &count, sizeof(long long));
// count now has the (approximated) result

// close the file descriptor
close(fd);