参数调优为什么要采样_性能优化-一个命令发现性能问题

本文转载自微信公众号「编程珠玑」,作者守望先生 。转载本文请联系编程珠玑公众号。

原文链接:https://mp.weixin.qq.com/s/pZtqz2tl4ArbCnzdDdDPwQ

为了取得程序的一丁点性能提升而大幅度增加技术的复杂性和晦涩性能,这个买卖做不得,这不仅仅是因为复杂的代码容器滋生bug,也因为他会使日后的阅读和维护工作要更加艰难。

为什么要性能优化

也许是想要支持更高的吞吐量,想要更小的延迟,或者提高资源的利用率等,这些都是性能优化的目标之一。不过需要提醒的是,不要过早的进行性能优化。如果当前并没有任何性能问题,又何必耗费这个精力呢?当前一些有助于提高性能的编码习惯还是可以时刻保持的。

目标

全面的性能优化不是一件简单的事情。本系列文章不在于介绍性能优化原理或者特定的算法优化。旨在分享一些实践中常用到的技巧,同时也主要关注CPU方面。

如何发现性能瓶颈

解决性能问题的第一步是发现性能问题。如何快速发现性能问题呢?对于本文来说,如何发现那些使CPU不停地瞎忙的代码呢?为什么这里是说让CPU瞎忙的代码?

举个例子,完成某个事情,你可能只需要一个CPU时间片,但是由于代码不够好,使得仍然需要多个CPU时间片。导致CPU非常忙碌,而无法继续提高它的效率。

top

这个命令相信大家都用过,可以实时看到进程的一些状态。它的使用方法有很多文章不厌其烦地对其进行了介绍,本文不打算进行介绍。我们可以通过top命令看到某个进程占用的CPU,但是CPU占用高并不代表它有性能问题,也有可能是CPU正在有效地高速运转,并没有占着茅坑不拉屎。

快速发现

想必我们都听过八二法则,同样的,80%的性能问题集中于20%的代码。因此我们只要找到这20%的部分代码,就可以有效地解决一些性能问题。

本文使用perf命令,它很强大,支持的参数也非常多,不过没关系,本文也没打算全部介绍。

系统中可能没有perf命令,ubuntu可以使用如下方法安装:

sudo apt install linux-tools-common

实例

直接来看示例吧。例子很简单,只是将字符串的字母转为大写罢了。当然了,很多人可能一眼就看出了哪里有性能问题,不过没关系,这个例子只是为了说明perf的应用。

//来源:公众号【编程珠玑】

//作者:守望先生

//toUpper.c

#include

#include

#include

#include

#include

#include

#define MAX_LEN  1024*1024

void printCostTime(struct timeval *start,struct timeval *end)

{

if(NULL== start ||NULL==end)

{

return;

}

long cost = (end->tv_sec - start->tv_sec) * 1000 + (end->tv_usec - start->tv_usec)/1000;

printf("cost time: %ld ms\n",cost);

}

intmain(void)

{

srand(time(NULL));

intmin='a';

intmax='z';

char*str = malloc(MAX_LEN);

//申请失败则退出

if(NULL== str)

{

printf("failed\n");

return-1;

}

unsigned inti = 0;

while(i 

{

str[i] = ( rand() % ( max-min) ) +min;

i++;

}

str[MAX_LEN - 1] = 0;

struct timeval start,end;

gettimeofday(&start,NULL);

for(i = 0;i 

{

str[i]  = toupper( str[i] );

}

gettimeofday(&end,NULL);

printCostTime(&start,&end);

free(str);

str = NULL;

return0;

}

编译成可执行程序并运行:

$ gcc -o toUpper toUpper.c

$ ./toUpper

这个时候我们用top查看结果发现toUpper程序占用CPU 100%:

$top-p `pidof toUpper`

PID USERPR  NI    VIRT    RES    SHR S  %CPU %MEMTIME+ COMMAND

24456 root       20   0    5248   2044    952 R 100.0  0.0   0:07.13 toUpper

打开另外一个终端,执行命令:

$ perftop-p `pidof toUpper`

Samples: 1K ofevent'cycles:ppp', Eventcount(approx.): 657599945

Overhead  Shared Object  Symbol

99.13%  libc-2.23.so   [.] strlen

0.19%  [kernel]       [k] perf_event_task_tick

0.11%  [kernel]       [k] prepare_exit_to_usermode

0.10%  libc-2.23.so   [.] toupper

0.09%  [kernel]       [k] rcu_check_callbacks

0.09%  [kernel]       [k] reweight_entity

0.09%  [kernel]       [k] task_tick_fair

0.09%  [kernel]       [k] native_write_msr

0.09%  [kernel]       [k] trigger_load_balance

0.00%  [kernel]       [k] native_apic_mem_write

0.00%  [kernel]       [k] __perf_event_enable

0.00%  [kernel]       [k] intel_bts_enable_local

其中pidof命令用于获取指定程序名的进程ID。

看到结果了吗?可以很清楚地看到,strlen函数占用了整个程序99%的CPU,那这个CPU的占用是否可以优化掉呢?我们现在都清楚,显然是可以的,在对每一个字符串进行大写转换时,都进行了字符串长度的计算,显然是没有必要,可以拿到循环之外的。

同时我们也关注到,这里面有很多符号可能完全没见过,不知道什么含义了,比例如reweight_entity,不过我们知道它前面有着kernel字样,因此也就明白,这是内核干的事情,仅此而已。

这里实时查看的方法,当然你也可以保存信息进行查看。

$ perf record -e cycles -p `pidof toUpper` -g -a

执行上面的命令一段时间,用于采集相关性能和符号信息,随后ctrl+c中止。默认当前目录下生成perf.data,不过这里面的数据不易阅读,因此执行:

$ perf report

+  100.00%     0.00%  toUpper  [unknown]          [k] 0x03ee258d4c544155

+  100.00%     0.00%  toUpper  libc-2.23.so       [.] __libc_start_main

+   99.72%    99.34%  toUpper  libc-2.23.so       [.] strlen

0.21%     0.02%  toUpper  [kernel.kallsyms]  [k] apic_timer_interrupt

0.19%     0.00%  toUpper  [kernel.kallsyms]  [k] smp_apic_timer_interrupt

0.16%     0.00%  toUpper  [kernel.kallsyms]  [k] ret_from_intr

0.16%     0.00%  toUpper  [kernel.kallsyms]  [k] hrtimer_interrupt

0.16%     0.00%  toUpper  [kernel.kallsyms]  [k] do_IRQ

0.15%     0.15%  toUpper  libc-2.23.so       [.] toupper

0.15%     0.00%  toUpper  [kernel.kallsyms]  [k] handle_irq

0.15%     0.00%  toUpper  [kernel.kallsyms]  [k] handle_edge_irq

0.15%     0.00%  toUpper  [kernel.kallsyms]  [k] handle_irq_event

0.15%     0.00%  toUpper  [kernel.kallsyms]  [k] handle_irq_event_percpu

0.14%     0.00%  toUpper  [kernel.kallsyms]  [k] __handle_irq_event_percpu

0.14%     0.01%  toUpper  [kernel.kallsyms]  [k] __hrtimer_run_queues

0.13%     0.00%  toUpper  [kernel.kallsyms]  [k] _rtl_pci_interrupt

其中-g参数为了保存调用调用链,-a表示保存所有CPU信息。

因此就可以看到采样信息了,怎么样是不是很明显,其中的+部分还可以展开,看到调用链。

例如展开的部分信息如下:

-  100.00%     0.00%  toUpper  libc-2.23.so       [.] __libc_start_main

- __libc_start_main

99.72% strlen

当然了,实际上你也可以将结果重定向到另外一个文件,便于查看:

$ perf report > result

$ more result

# Event count(approx.): 23881569776

#

# Children      Self  Command  Shared Object      Symbol

# ........  ........  .......  .................  ..............................

...................

#

100.00%     0.00%  toUpper  [unknown]          [k] 0x03ee258d4c544155

|

---0x3ee258d4c544155

__libc_start_main

|

--99.72%--strlen

100.00%     0.00%  toUpper  libc-2.23.so       [.] __libc_start_main

|

---__libc_start_main

|

--99.72%--strlen

99.72%    99.34%  toUpper  libc-2.23.so       [.] strlen

|

--99.34%--0x3ee258d4c544155

这样看也是非常清晰的。

不过不要高兴地太早,并不是所有情况都能清晰的看到具体问题在哪里的。

至于本文例子的性能问题怎么解决,相信你已经很清楚了,只需要把strlen提到循环外即可,这里不再赘述。

总结

本文的例子过于简单粗暴,但是足够说明perf的使用,快速发现程序中占用CPU较高的部分,至于该部分能否被优化,是否正常就需要进一步分析了。不过别急,后续将会分享一些常见的可优化的性能点。

作者:守望,linux应用开发者,目前在公众号【编程珠玑】 分享Linux/C/C++/数据结构与算法/工具等原创技术文章和学习资源。

【编辑推荐】

【责任编辑:武晓燕 TEL:(010)68476606】

点赞 0

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值