命令
strace -c 程序
使用该命令即可获取该程序执行过程中每一系统调用的所执行的时间,次数和出错的次数等。
结果及分析
记录器
记录器结果
strace: Process 22088 detached
% time seconds usecs/call calls errors syscall
------ ----------- ----------- --------- --------- ----------------
100.00 0.143984 23997 6 1 futex
0.00 0.000000 0 7 read
0.00 0.000000 0 9 write
0.00 0.000000 0 7 close
0.00 0.000000 0 24 21 stat
0.00 0.000000 0 9 fstat
0.00 0.000000 0 1 lseek
0.00 0.000000 0 21 mmap
0.00 0.000000 0 16 mprotect
0.00 0.000000 0 1 munmap
0.00 0.000000 0 3 brk
0.00 0.000000 0 3 rt_sigaction
0.00 0.000000 0 1 rt_sigprocmask
0.00 0.000000 0 1 ioctl
0.00 0.000000 0 1 writev
0.00 0.000000 0 7 7 access
0.00 0.000000 0 7 shmget
0.00 0.000000 0 7 shmat
0.00 0.000000 0 4 clone
0.00 0.000000 0 1 execve
0.00 0.000000 0 1 arch_prctl
0.00 0.000000 0 1 gettid
0.00 0.000000 0 1 set_tid_address
0.00 0.000000 0 42 34 openat
0.00 0.000000 0 1 set_robust_list
0.00 0.000000 0 1 prlimit64
------ ----------- ----------- --------- --------- ----------------
100.00 0.143984 183 63 total
记录器分析
占比时间最多的系统调用为futex,到达了100%的系统调用时间占用,共计0.14s,futex是和互斥锁相关的系统调用,由于多个线程要对同一个队列进行读写,因此我使用了互斥锁,这么来看互斥锁的效率还是非常高的,效果完全在可以接收的范围内。而write系统调用仅使用了7次,说明boost的过滤流是自带缓冲区的,当缓冲区满时才会真正调用write系统调用。
Futex 是Fast Userspace muTexes的缩写
Futex是一种用户态和内核态混合的同步机制。首先,同步的进程间通过mmap共享一段内存,futex变量就位于这段共享的内存中且操作是原子的,当进程尝试进入互斥区或者退出互斥区的时候,先去查看共享内存中的futex变量,如果没有竞争发生,则只修改futex,而不用再执行系统调用了。当通过访问futex变量告诉进程有竞争发生,则还是得执行系统调用去完成相应的处理(wait 或者 wake up)。简单的说,futex就是通过在用户态的检查,(motivation)如果了解到没有竞争就不用陷入内核了,大大提高了low-contention时候的效率。
通过查阅资料,了解了futex系统调用少的原因,其设计出发点就是为了解决原本IPC通信频繁陷入内核,上下文切换开销太大的缺点,这也说明c++11的mutex底层是依赖于futex实现的,是非常高效的。
播放器
播放器结果
下面的结果是我追踪我的数据包播放器的系统调用情况:
% time seconds usecs/call calls errors syscall
------ ----------- ----------- --------- --------- ----------------
100.00 0.051993 12998 4 futex
0.00 0.000000 0 8 read
0.00 0.000000 0 7 write
0.00 0.000000 0 8 close
0.00 0.000000 0 25 21 stat
0.00 0.000000 0 9 fstat
0.00 0.000000 0 2 lseek
0.00 0.000000 0 19 mmap
0.00 0.000000 0 14 mprotect
0.00 0.000000 0 1 munmap
0.00 0.000000 0 3 brk
0.00 0.000000 0 3 rt_sigaction
0.00 0.000000 0 1 rt_sigprocmask
0.00 0.000000 0 1 ioctl
0.00 0.000000 0 7 7 access
0.00 0.000000 0 2 clone
0.00 0.000000 0 1 execve
0.00 0.000000 0 1 arch_prctl
0.00 0.000000 0 1 gettid
0.00 0.000000 0 1 set_tid_address
0.00 0.000000 0 42 34 openat
0.00 0.000000 0 1 set_robust_list
0.00 0.000000 0 1 prlimit64
------ ----------- ----------- --------- --------- ----------------
100.00 0.051993 162 62 total
播放器分析
可以看到,占用了较多时间的系统调用仍旧为futex,和记录器是差不多的。
结论
从系统调用上来看,无论是最开始担心的互斥量竞争,还是new 和 delete的频繁使用(对应mmap系统调用和brk系统调用),都未消耗过多的时间,可以忽略不计,暂时没有可优化的地方。不过如果想优化的话,可以尝试实现一个内存池降低系统调用,另外,互斥量导致的系统调用虽然在总时间上比较少,但一旦出现竞争,每次耗费的时间都在0.01-0.02秒附近,这是非常高的开销,如何减少互斥锁的使用也应该考虑。