目录:
1、任务调度带来任务切换的开销 | ||
---|---|---|
perf性能检测工具的安装 | 栈空间速度比堆空间快的原因 | 例:线程池 |
2、系统调用带来用户态和内核切换的开销 | ||
3、代码级别优化 | ||
热点函数,文件收集 | 性能优化工具 |
优化
1. 算法优化,选择优秀的数据结构和算法。
2. 代码优化,提高执行速度,减少内存占用。
系统级优化
更加有效地利用硬件和操作系统资源
- 上下文切换次数评估 —> 降低OS开销
- 缺页异常次数评估 —> 减少页面交换
- IPC 评估 —> 提高CPU利用率
如何做优化?
对症下药:系统性能瓶颈在哪里?
提高cpu使用效率。做更多计算任务
1、任务调度带来任务切换的开销
避免不必要的上下文切换
引起上下文切换的原因:
-
时间片用完,CPU正常调度下一个任务
-
被其他优先级更高的任务抢占
-
执行任务碰到IO阻塞,调度器挂起当前任务,切换执行下一个任务
-
用户代码主动挂起当前任务让出CPU时间( pthread_cond_wait )
-
多任务抢占资源,由于没有抢到被挂起(mutex )
(加锁的粒度要细化) -
硬件中断/软中断
perf性能检测工具
perf性能检测工具的安装
sudo apt install linux-tools-common
sudo apt install linuex-tools-4.15***-generic
sudo apt install linux-tools-generic
输入sudo perf stat ls,执行ls命令,收集特定进程的性能概况,
[yola2626@ubuntu ~]$sudo perf stat ls
//ls执行的打印...
Performance counter stats for 'ls':
// 任务真正占用的处理器时间,单位为 ms。
0.94 msec task-clock # 0.309 CPUs utilized //CPU 的占用率。
// 程序在运行过程中上下文的切换次数
21 context-switches # 0.022 M/sec
// 程序在运行过程中发生的处理器迁移次数。
0 cpu-migrations # 0.000 K/sec
// 缺页异常的次数
100 page-faults # 0.107 M/sec
<not supported> cycles
<not supported> instructions
<not supported> branches
<not supported> branch-misses
//花费时间
0.003022606 seconds time elapsed
0.000000000 seconds user
0.001261000 seconds sys
CPU-migrations:程序在运行过程中发生的处理器迁移次数。
Linux为了维持多个处理器的负载均衡, 在特定条件下会将某个任务从一个CPU迁移到另一个CPU。
CPU迁移和上下文切换:
发生上下文切换不一定会发生CPU迁移,而发生CPU迁移时肯定会发生上下文切换。发生上下文切换有可能只是把上下文从当前CPU中换出,下一次调度器还是将进程安排在这个 CPU上执行。
page-faults:缺页异常的次数
-
当应用程序请求的页面尚未建立、请求的页面不在内存中
-
请求的页面虽然在内存中,但物理地址和虚拟地址的映射关系尚未建立时,都会触发一次缺页异常
-
另外 TLB 不命中,页面访问权限不匹配等情况也会触发缺页异常。
栈空间速度比堆空间快的原因
1. 栈空间在申请空间时是4K的整数倍,跟物理页是对等的,缺页异常次数较少
2. 硬件上,栈顶指针时刻在寄存里,不会从寄存器唤出,效率高
物理内存不足
LRU (最近最少使用算法)
比较冷的物理页 — 放入 Windows 虚拟内存 / Linux swap分区 (缺页异常)
TLB快表
| 虚拟地址 | 物理地址|
| | |
TLB未命中,通过多级页表进行翻译得到物理地址
cycles:消耗的处理器周期数。
如果把被 ls 使用的 cpu cycles 看成是一个处理器的,那么它的主频可以用cycles / task-clock算出。
stalled-cycles-frontend:
指令读取或解码的质量步骤,未能按理想状态发挥并行左右,发生停滞的时钟周期。
stalled-cycles-backend: 指令执行步骤,发生停滞的时钟周期。
instructions: 执行了多少条指令。IPC为平均每个cpu cycle执行了多少条指令。
branches: 遇到的分支指令数。branch-misses是预测错误的分支指令数。
避免锁引起的调度,实现无锁设计
例:线程池
上下文切换开销
- 主线程 和 子线程 来回切换执行
- mutex 加解锁
情景1:
服务器16个核
1000个用户,每人下载10M大小的文件
最短时间
- 创建多少个线程数(16 / 1000)— 17 (16个线程 自动均衡到每个核 )
- 怎么设计加解锁 — 16个队列 锁的粒度 加解锁次数少
情景2:
1000个用户,每人下载100M大小的文件 每人时刻都在下载文件
63 new_fd 分给一个线程
假如使用
for()
{
发给每个用户
}
结构体
struct{
int new_fd;
int pos; //mmap对应地址下标
}
2、系统调用带来用户态和内核切换的开销
查看使用 ls 系统调用次数
[yola2626@ubuntu ~]$ sudo perf stat -e raw_syscalls:sys_enter ls
Performance counter stats for 'ls':
94 raw_syscalls:sys_enter
0.003045539 seconds time elapsed
0.001715000 seconds user
0.000000000 seconds sys
系统调用造成用户态上下文栈压栈弹栈,有损耗。降低系统调用使用次数
例如:
-
反复的read,write,就改为mmap,消息队列没有共享内存实现的消息队列快
-
多次的malloc可以升级为一次malloc
-
持久使用一块内存可以用大页 4K 2M 1G hugepages 避免发生tlb miss
-
通过使用 sendfile及splice 降低系统调用带来的开销 零COPY
(进程池sendfile和splice)
3、代码级别优化
热点函数,文件收集
使用示例程序:
#include <stdio.h>
#define N 1000000
void cal()
{
int i = 0;
for(i = 0; i < 100; i++)
{
}
}
int main()
{
int i;
printf("I am printf\n");
for(i = 0; i < N; i++)
{
cal();
}
}
使用 perf 指令
//生成perf.data文件
sudo perf record -g ./a.out
//读取perf record生成的perf.data文件,并显示分析数据
sudo perf report -i perf.data
得到
Samples: 158 of event 'cpu-clock:pppH', Event count (approx.): 39500000
Children Self Command Shared Object Symbol
+ 100.00% 0.00% a.out [unknown] [.] 0x0706258d4c544155 ?
+ 100.00% 0.00% a.out libc-2.27.so [.] __libc_start_main ?
+ 99.37% 1.90% a.out a.out [.] main ?
+ 98.10% 98.10% a.out a.out [.] cal
热点函数为 cal
高频的文件打开,需要持久打开,或者常驻内存
打开以后,队列里面去
Mysql数据库 访问过多,拒绝访问,怎么办?
100个连接,队列机制,建立连接池( 队列 )
使用 Redis — 内存数据库( Redis 是一个高性能的key-value数据库。 )【Redis】
lsof (命令)统计系统打开的所有文件
函数热点分支收集
性能路径频繁调用的小函数内联化
修改为static inline
实现函数内联 (函数前加static inline
)
static inline void cal()
cache tlb 及分支预取优化
http://www.cnblogs.com/lirunzhou/p/5883698.html http://blog.csdn.net/adam040606/article/details/49563453
性能优化工具列表
perf,oprofile,gprof,systemtap
linux有许多非常好用的性能分析工具,这里是最常用的几个:
- perf 应该是最全面最方便的一个性能检测工具。由linux内核携带并且同步更新,基本能满足日常使用。推荐使用。
- oprofile 差不多是一个较过时的性能检测工具了,基本被 perf 取代,命令使用起来也不太方便。 比如opcontrol –no-vmlinux , opcontrol –init 等命令启动,然后是 opcontrol –start, opcontrol – dump, opcontrol -h 停止,opreport 查看结果等,一大串命令和参数。有时候使用还容易忘记初始化,数据就是空的。
- gprof 主要是针对应用层程序的性能分析工具,缺点是需要重新编译程序,而且对程序性能有一些 影响。不支持内核层面的一些统计,优点就是应用层的函数性能统计比较精细,接近我们对日常性 能的理解,比如各个函数时间的运行时间,,函数的调用次数等,很人性易读。
- systemtap 其实是一个运行时程序或者系统信息采集框架,主要用于动态追踪,当然也能用做性 能分析,功能最强大,同时使用也相对复杂。不是一个简单的工具,可以说是一门动态追踪语言。 如果程序出现非常麻烦的性能问题时,推荐使用 systemtap。
Gcov,lcov 统计代码覆盖率
工具 | 功能 |
---|---|
gcov | 生成代码覆盖信息 |
lcov | 把文字的代码覆盖率信息转换成更直观的图形表格模式 |
Benchmark
Benchmark简介
Benchmark是一个评价方式,在整个计算机领域有着长期的应用。Benchmark在计算机领域应用最成功的就是性能测试,主要测试负载的执行时间、传输速度、吞吐量、资源占用率等。
性能调优的两大利器是 Benchmark 和 profile工具。Benchmark用压力测试挖掘整个系统的性能状况,而profile工具最大限度地呈现系统的运行状态和性能指标,方便用户诊断性能问题和进行调优。
Benchmark的组成
Benchmark的核心由3部分组成:数据集、 工作负载、度量指标。
kememleak 内存泄漏检测工具
memcheck 内存检查工具
valgrind 检测内存泄漏
推荐的一些文章: