Performance 分析readdir()性能

Performance 分析readdir()性能,performance,readdir,Performance,Readdir,linux需要很长时间才能列出大型目录的所有文件,这让我很困扰,因此我创建了一个小测试脚本,递归地列出目录中的所有文件: #include <stdio.h> #include <dirent.h> int list(char *path) { int i = 0; DIR *dir = opendir(path); struct dirent *entry; char new_path[1024]; while(entry

linux需要很长时间才能列出大型目录的所有文件,这让我很困扰,因此我创建了一个小测试脚本,递归地列出目录中的所有文件:

#include <stdio.h>
#include <dirent.h>

int list(char *path) {
    int i = 0;

    DIR *dir = opendir(path);
    struct dirent *entry;
    char new_path[1024];

    while(entry = readdir(dir)) {
        if (entry->d_type == DT_DIR) {
            if (entry->d_name[0] == '.')
                continue;

            strcpy(new_path, path);
            strcat(new_path, "/");
            strcat(new_path, entry->d_name);
            i += list(new_path);
        }
        else
            i++;
    }
    closedir(dir);

    return i;
}

int main() {
    char *path = "/home";
    printf("%i\n", list(path));
    return 0;
因此,它在内核空间中花费大约S=0.3秒,在用户空间中花费大约U=0.01秒,并且有7+1692个上下文切换。 上下文切换大约需要2000nsec*(7+1692)=3.398ms[1] 然而,还有10多秒,我想知道这个程序在这段时间里做了什么。 有没有其他工具来调查程序一直在做什么? gprof只是告诉我(用户空间)调用图的时间,gcov没有列出每行花费的时间,只列出执行时间的频率


[1]

oprofile
是一个不错的采样分析器,它可以分析用户和内核模式代码

然而,根据您的数字,大约有14.5秒的时间用于睡眠,而
oprofile
并没有很好地记录这一点。也许更有用的是结合读取内核代码
ftrace
在内核中提供跟踪点,可以记录消息并在消息发生时堆叠跟踪。对于确定进程为何休眠最有用的事件是
sched_开关
事件。我建议您启用内核模式堆栈和
sched_开关
事件,设置足够大的缓冲区以捕获进程的整个生命周期,然后运行进程并立即停止跟踪。通过查看跟踪,您将能够看到每次进程进入睡眠状态时,它是可运行的还是不可运行的,一个高分辨率的时间戳,以及一个指示是什么使它进入睡眠状态的调用堆栈

ftrace
通过
debugfs
控制。在我的系统上,它安装在
/sys/kernel/debug
中,但您的可能不同。下面是一个我将如何捕获此信息的示例:

# Enable stack traces
echo "1" > /sys/kernel/debug/tracing/options/stacktrace
# Enable the sched_switch event
echo "1" > /sys/kernel/debug/tracing/events/sched/sched_switch/enable
# Make sure tracing is enabled
echo "1" > /sys/kernel/debug/tracing/tracing_on
# Run the program and disable tracing as quickly as possible
./your_program; echo "0" > /sys/kernel/debug/tracing/tracing_on
# Examine the trace
vi /sys/kernel/debug/tracing/trace
结果输出将具有如下所示的行:

# tracer: nop
#
# entries-in-buffer/entries-written: 22248/3703779   #P:1
#
#                              _-----=> irqs-off
#                             / _----=> need-resched
#                            | / _---=> hardirq/softirq
#                            || / _--=> preempt-depth
#                            ||| /     delay
#           TASK-PID   CPU#  ||||    TIMESTAMP  FUNCTION
#              | |       |   ||||       |         |
          <idle>-0     [000] d..3  2113.437500: sched_switch: prev_comm=swapper/0 prev_pid=0 prev_prio=120 prev_state=R ==> next_comm=kworker/0:0 next_pid=878 next_prio=120
          <idle>-0     [000] d..3  2113.437531: <stack trace>
 => __schedule
 => schedule
 => schedule_preempt_disabled
 => cpu_startup_entry
 => rest_init
 => start_kernel
     kworker/0:0-878   [000] d..3  2113.437836: sched_switch: prev_comm=kworker/0:0 prev_pid=878 prev_prio=120 prev_state=S ==> next_comm=your_program next_pid=898 next_prio=120
     kworker/0:0-878   [000] d..3  2113.437866: <stack trace>
 => __schedule
 => schedule
 => worker_thread
 => kthread
 => ret_from_fork
#跟踪器:nop
#
#缓冲区中的条目/写入的条目:22248/3703779#P:1
#
#-----=>IRQ关闭
#/"--=>需要重新扫描吗
#|/--=>硬件/软件
#| |/|--=>抢占深度
#| | | |/延迟
#TASK-PID CPU#| | | |时间戳函数
#              | |       |   ||||       |         |
-0[000]d..3 2113.437500:sched_开关:prev_comm=swapper/0 prev_pid=0 prev_prio=120 prev_state=R==>next_comm=kworker/0:0 next_pid=878 next_prio=120
-0[000]d..3 2113.437531:
=>\u时间表
=>时间表
=>计划\u抢占\u禁用
=>cpu\u启动\u项
=>rest\u init
=>启动内核
kworker/0:0-878[000]d..3 2113.437836:sched_开关:prev_comm=kworker/0:0 prev_pid=878 prev_prio=120 prev_state=S=>next_comm=your_program next_pid=898 next_prio=120
kworker/0:0-878[000]d..3 2113.437866:
=>\u时间表
=>时间表
=>辅助线程
=>kthread
=>ret_从_分叉

当您的程序显示为
prev_comm
任务时,您需要注意的是,这意味着计划程序正在从您的程序切换到运行其他程序
prev_state
将指示您的程序仍然可以运行(
R
)或被阻止(
S
U
或其他字母,请参阅ftrace源代码)。如果被阻止,您可以检查堆栈跟踪和内核源代码以找出原因。

大多数情况下(1000倍)prev_状态是
D|K
,但是,我找不到任何源代码告诉我这意味着什么。到值的映射在此处定义:,加上sched.h中常量的定义,表明任务的大部分时间都处于
task\u untruptible
task\u WAKEKILL
状态。现在,您应该查看其中一些调用堆栈,并确定其阻塞的位置。我看您已经接受了这个答案。这种方法是否有助于您解决(或至少理解)问题?
# tracer: nop
#
# entries-in-buffer/entries-written: 22248/3703779   #P:1
#
#                              _-----=> irqs-off
#                             / _----=> need-resched
#                            | / _---=> hardirq/softirq
#                            || / _--=> preempt-depth
#                            ||| /     delay
#           TASK-PID   CPU#  ||||    TIMESTAMP  FUNCTION
#              | |       |   ||||       |         |
          <idle>-0     [000] d..3  2113.437500: sched_switch: prev_comm=swapper/0 prev_pid=0 prev_prio=120 prev_state=R ==> next_comm=kworker/0:0 next_pid=878 next_prio=120
          <idle>-0     [000] d..3  2113.437531: <stack trace>
 => __schedule
 => schedule
 => schedule_preempt_disabled
 => cpu_startup_entry
 => rest_init
 => start_kernel
     kworker/0:0-878   [000] d..3  2113.437836: sched_switch: prev_comm=kworker/0:0 prev_pid=878 prev_prio=120 prev_state=S ==> next_comm=your_program next_pid=898 next_prio=120
     kworker/0:0-878   [000] d..3  2113.437866: <stack trace>
 => __schedule
 => schedule
 => worker_thread
 => kthread
 => ret_from_fork