C语言 对于perf_event_open,mmap()比read()快吗

wfypjpf4  于 2023-10-16  发布在  其他
关注(0)|答案(1)|浏览(92)

我希望在我的应用程序中监视一些事件(硬件,软件和硬件缓存)。与大多数进行性能分析的应用程序一样,性能是关键。在一个理想的世界里,我将能够直接读取CPU PMU事件的CPU周期计数和其他使用mrs指令。但是由于内核默认情况下禁止从EL0访问PMU,所以我的应用程序只能使用perf。
就目前而言,我下面的read_values()函数使用read()读取结果。我一直在寻找加快这些性能事件值检索的方法,并偶然发现了这个PMU HW计数器访问文档。
我的问题有两个方面:
1.使用mmap()而不是read()来从fd中检索值是否是一种性能改进?如果是这样,我将如何做到这一点?
1.有没有一种方法可以使用mrs instructor直接检索PMU注册表?从上面的PMU HW计数器访问链接,它指出这应该是可能的。虽然我很难找到解释如何做到这一点的例子。

struct read_format {
  uint64_t nr;          /* The number of events */
  struct {
    uint64_t value;     /* The value of the event */
    uint64_t id;        /* if PERF_FORMAT_ID */
  } values[nr];
};

int main() {
  struct perf_event_attr attr1;
  attr1.type = PERF_TYPE_HARDWARE;
  attr1.config = PERF_COUNT_HW_CPU_CYCLES;
  attr1.read_format = PERF_FORMAT_GROUP | PERF_FORMAT_ID;
  int main_fd = syscall(__NR_perf_event_open, &attr1, 0, -1, -1, 0);
  uint64_t id1;
  ioctl(main_fd, PERF_EVENT_IOC_ID, &id1);
  ioctl(main_fd, PERF_EVENT_IOC_RESET, 0);
  ioctl(main_fd, PERF_EVENT_IOC_ENABLE, 0);

  struct perf_event_attr attr2;
  attr2.type = PERF_TYPE_HARDWARE;
  attr2.config = PERF_COUNT_HW_CACHE_REFERENCES;
  attr2.read_format = PERF_FORMAT_GROUP | PERF_FORMAT_ID;
  int fd2 = syscall(__NR_perf_event_open, &attr2, 0, -1, main_fd, 0);
  uint64_t id2;
  ioctl(fd2, PERF_EVENT_IOC_ID, &id2);
  ioctl(fd2, PERF_EVENT_IOC_RESET, 0);
  ioctl(fd2, PERF_EVENT_IOC_ENABLE, 0);

  // read_values and log "START"

  // action

  // read_values and log "END"

  return 0;
}

read_values() {
  char buffer[4096];
  int read_bytes = read(main_fd, &buffer, sizeof(buffer));
  if (read_bytes == -1) { return 1; }

  struct read_format* rf = (struct read_format*) buffer;
  int values[rf->nr];
  for (int i=0; i<rf->nr; i++) {
    values[i] = rf->values[i].value;
  }
}

read_values() read()调用更改为mmap(NULL, sizeof(read_format), PROT_READ, MAP_SHARED, main_fd, 0)不起作用。在mmap调用后阅读回缓冲区时,缓冲区没有填充,因为事件数设置为0。

read_values() {
  char* buffer = read(main_fd, &buffer, sizeof(buffer));
  if (buffer == MAP_FAILED) { return 1; }

  struct read_format* rf = (struct read_format*) buffer;
  if (rf->nr == 0) { return 1; }
  int values[rf->nr];
  for (int i=0; i<rf->nr; i++) {
    values[i] = rf->values[i].value;
  }
}
csbfibhn

csbfibhn1#

mmap()read()没有任何关系。mmap()允许您Map内存中的文件,但底层软件使用与read()实际使用的完全相同的机制从磁盘读取文件(它将块移动到内存块中,以允许多个进程访问磁盘的同一文件)。
唯一的区别(这可能使mmap()成为比read()更好的方法)是它直接将内核缓冲区(文件内容)Map到项目的虚拟内存空间。但是这并不能自动地使它更快,因为对用于Map的内存的访问仍然需要由内核监控(在每次访问时),当你在分配给缓冲区的空间中写入时,将文件缓冲区标记为脏(并尊重你只在数据区中写入,并且从不在内务数据中)当inode被另一个对文件发出read()write()访问的进程阻塞时,您的进程也必须被阻塞,以避免在其他进程锁定inode时访问数据。
这意味着必须对您写入的内存进行一些Map,因此它实际上是写入对应于文件的内核缓冲区中,并且必须使用与read中的copy_from/copy_to复制类似的机制来完成。事情被优化以快速完成,但平均增益不是内存访问对磁盘访问的增益。
此外,该文件必须是可能的mmap()这不是一个一般的情况下,例如。NFS文件不能被Map(或者说不能很容易地Map),套接字和管道不能被Map,设备也不能被Map(这取决于情况,需要设备驱动程序的一些协作来完成)。一般来说,不能为lseek() d的文件将无法被Map。

相关问题