代码之家  ›  专栏  ›  技术社区  ›  Serge Rogatch

以编程方式启用/禁用性能事件集合

  •  0
  • Serge Rogatch  · 技术社区  · 3 年前

    我正在使用 perf 用于在Ubuntu 20.04上进行评测(尽管我可以使用任何其他免费工具)。它允许在CLI中传递延迟,以便在程序启动后的特定时间后开始事件收集。然而,这个时间变化很大(1000秒中有20秒),还有一些我也不感兴趣的尾部计算。

    因此,从我的程序中调用一些API来启动会很好 perf 事件收集我感兴趣的代码片段,然后在代码完成后停止收集。

    在循环中运行代码并不是一个真正的选择,因为有大约30秒的初始化阶段和10秒的测量阶段,我只对后者感兴趣。

    0 回复  |  直到 3 年前
        1
  •  4
  •   Peter Cordes Steve Bohrer    2 年前

    有一种进程间通信机制可以在正在分析的程序(或控制进程)和性能进程之间实现这一点:使用 --control 格式中的选项 --control=fifo:ctl-fifo[,ack-fifo] --control=fd:ctl-fd[,ack-fd] 如中所述 perf-stat(1) 手册页。此选项指定一对FIFO文件的路径名(命名管道)或一对文件描述符。第一个文件用于发出命令,以启用或禁用正在侦听同一文件的任何perf进程中的所有事件。第二个文件是可选的,用于在perf实际执行命令时进行检查。

    手册页中有一个示例,展示了如何使用此选项从bash脚本控制perf进程,您可以很容易地将其转换为C/C++:

    ctl_dir=/tmp/
    
    ctl_fifo=${ctl_dir}perf_ctl.fifo
    test -p ${ctl_fifo} && unlink ${ctl_fifo}
    mkfifo ${ctl_fifo}
    exec ${ctl_fd}<>${ctl_fifo}        # open for read+write as specified FD
    

    这首先检查文件 /tmp/perf_ctl.fifo ,如果存在,是一个命名管道,然后它才会删除它。如果该文件不存在,这不是问题,但如果它存在,并且不是命名管道,则不应删除该文件,并且 mkfifo 应该失败。这个 mkfifo 使用路径名创建命名管道 /tmp/perf_ctl.fifo 。然后,下一个命令打开具有读/写权限的文件,并将文件描述符分配给 ctl_fd 。等效的系统调用是 fstat , unlink , mkfifo open 。请注意,命名管道将由shell脚本(控制进程)或正在分析的进程写入,并将从perf进程中读取。对第二个命名管道重复相同的命令, ctl_fd_ack ,将用于接收来自perf的确认。

    perf stat -D -1 -e cpu-cycles -a -I 1000       \
              --control fd:${ctl_fd},${ctl_fd_ack} \
              -- sleep 30 &
    perf_pid=$!
    

    这会分叉当前进程并运行 perf stat 子进程中的程序,该程序继承相同的文件描述符。这个 -D -1 选项告诉perf在禁用所有事件的情况下启动。您可能需要更改perf选项,如下所示:

    perf stat -D -1 -e <your event list> --control fd:${ctl_fd},${ctl_fd_ack} -p pid
    

    在这种情况下,要评测的程序与控制进程相同,因此请告诉perf使用 -p 。等效的系统调用是 fork 然后 execv 在子进程中。

    sleep 5  && echo 'enable' >&${ctl_fd} && read -u ${ctl_fd_ack} e1 && echo "enabled(${e1})"
    sleep 10 && echo 'disable' >&${ctl_fd} && read -u ${ctl_fd_ack} d1 && echo "disabled(${d1})"
    

    示例脚本睡眠约5秒,将“enable”写入 ctl_fd pipe,然后检查来自perf的响应以确保事件已启用,然后在大约10秒后继续禁用事件。等效的系统调用是 write read .

    脚本的其余部分将删除文件描述符和管道文件。

    现在把所有这些放在一起,你的程序应该是这样的:

    /* PART 1
    Initialization code.
    */
    
    /* PART 2
    Create named pipes and fds.
    Fork perf with disabled events.
    perf is running now but nothing is being measured.
    You can redirect perf output to a file if you wish.
    */
    
    /* PART 3
    Enable events.
    */
    
    /* PART 4
    The code you want to profile goes here.
    */
    
    /* PART 5
    Disable events.
    perf is still running but nothing is being measured.
    */
    
    /* PART 6
    Cleanup.
    Let this process terminate, which would cause the perf process to terminate as well.
    Alternatively, use `kill(pid, SIGINT)` to gracefully kill perf.
    perf stat outputs the results when it terminates.
    */