10:59 am
Tuesday, 1 November 2022 (HKT)
Time in Hong Kong


背景

我们的设备经过性能测试,在IO测试阶段、数据库操作部分的跑分显著与对比机拉开差距,且跑分结果怪异:常规的串行读写、随机读写与对比机成绩差距细微,但是数据库操作居然差距巨大

数据库跑分对比

目标

从表中可以看到,两者常规IO成绩接近,但是数据库事务差距一倍以上。我们需要分析跑分差异的原因,并定位设备和系统的性能瓶颈。

  1. 分析跑分差异根因;为什么数据库读写会意外地落差巨大?
  2. 定位性能瓶颈;性能瓶颈是来自存储吗?

环境、工具

环境

设备 软件环境 硬件环境 跑分手法
MT8183 Android 10 + 不加后台负载 MT8183 + 4GB LPDDR4X + mmc AndroBench V5.0.1 测试data分区,数据库事务size=1; 启用Index Usage; Journal Mode=WAL;
对比机 QCOM 660 AIE Android 9 + 不加后台负载 QCOM 660 AIE + 4GB LPDDR4 同上

注意,IO测试与存储芯片有相关性,这里用mmc来表示,忽略具体的存储芯片型号和性能。

具体原因是:1. 没有取得机器们的存储芯片的准确的型号和性能参数;2. 两者在Antutu BenchMark和AndroBench的串行读写、随机读写的成绩几乎一致——因此可以认为两个机器的存储性能接近(起码是跑分场景的性能接近),所以忽略存储芯片差异,不影响分析

工具

工具 目的 使用
AndroBench V5.0.1 数据库跑分
strace 统计系统调用耗时 strace -qc -pPID
top 查看线程CPU、Per CPU Usage busybox top -d1 转H、转1
top 查看线程状态 top -m15 -H -pPID
iostat 看IO信息 busybox iostat -cdtzm 1
TraceView 看Trace
taskset 对跑分软件绑核 taskset -ap MASK PID
drop_caches 强制系统丢弃cache echo 1 > /proc/sys/vm/drop_caches
sync 写入脏buffer到磁盘 sync
CPU定频 让CPU运行在最高频 链接

观测

本节通过一些观测手法来尝试分析和定位性能瓶颈。

1. Trace上看瓶颈是存储

首先再次在机器上跑一次AndroBench数据库测试,并抓一次trace。

8183_perfetto.png

可以看到sys_fdatasync耗时很长,整个数据库事务提交过程绝大部分在fdatasync()。乍一看似乎瓶颈是存储性能,但由于trace机制粒度较粗,细节较少,其实目前还没有足够的证据证实。Anyway,先继续分析,暂时不做出性能瓶颈是存储的结论。

Trace上有很多层的sys_fdatasync,虽然它记录的耗时很长但是不一定能真实反应data sync耗时真的很长。实际上很可能是对fdatasync trace end的调用放到了整个过程的后面,即实际上sync工作完成之后一段时间才结束这个tag的trace,因而data sync的记录很长,且记录的耗时可能远远高于实际耗时。

  • 小结1:Trace目前没有可靠地提示一些信息

  • 小结2: 根据Trace的实现机制,认为该Trace记录可能不准确

1.1 检验fdatasync是否真的耗时

从Linux man可以查阅到,fdatasync()fsync()都是会阻塞直到数据完成写入的。那么我们可以通过检查IO线程的挂起状态的占比即可确认线程是否在等待IO时消耗了过长的时间。

The call blocks until the device reports that the transfer has completed。

x71ZTS.png

从trace可以看到,线程在IO等待方面耗时很小。

  • 小结:IO flush操作耗时不是性能瓶颈

2. 数据库测试没有跑满IO极限

从上面的分析来看,并没有严丝合缝的证据表明数据库事务测试碰到了IO瓶颈。本节通过一些方法检验该机器的数据库测试性能瓶颈不是IO,并设计一些实验来进行一些额外的观测。

与顺序/随机读写测试对比而言,数据库CRUD的特点是QPS高,但是数据量低。表现在IO上,前者消耗更多的IO资源,而后者通常需要高并发来取得高IO占用。

顺序/随机读写测试,消耗很低的CPU,触及IO极限:

顺序读写测试资源占用.png

  • 上图结果可见,该机器的IO极限约90附近。CPU消耗低且处于IO wait。下图为数据库跑分阶段:

数据库事务测试资源占用.png

  • 数据库CRUD测试远远达不到IO极限(仅峰值的10%+),但是需要付出巨大的CPU代价。注意,CPU消耗占比中,USER和SYS占比上升几倍,而IO wait接近可以忽略,表明此时CPU是“真忙”,而不是在等待IO中的“假忙”

  • 小结:数据库事务测试没有跑到IO极限,且针对该设备,数据库CRUD是计算资源敏感型

通常来说数据库性能除了与硬件相关,还与数据库库表设计、磁盘缓冲、内存操作容量等因素相关。但是在IO性能测试场景,由于和对比机使用的测试工具是一致的,因此需要忽略数据库的设计、操作方法、缓存方案、并发数、连接数,而重点关注其他的具有差异的因素对数据库性能的影响。如下:

  • 系统缓存策略
  • CPU性能
  • 存储性能

由于在数据库事务测试中发现没有跑满IO,因此尝试伸缩IO资源来检验性能瓶颈

2.1 系统缓冲区对dd性能的影响

Linux系统IO实现是分层的,一些上层的测试方法可能因缓冲区策略、大小不同而出现不同的测试成绩。本小结测试缓冲区对dd的性能影响。

iostat输出的数据实际上是磁盘真实IO速度,而不受缓冲区策略的影响

每次测试前,清空buffer,丢弃cache

1
2
3
4
5
6
7
8
9
/data/local/tmp # free -k && sync && echo 1 > /proc/sys/vm/drop_caches && free -k
total used free shared buffers
Mem: 3957988 1429360 2528628 7048 4464
-/+ buffers/cache: 1424896 2533092
Swap: 2176888 302080 1874808
total used free shared buffers
Mem: 3957988 1413012 2544976 7048 476
-/+ buffers/cache: 1412536 2545452
Swap: 2176888 302080 1874808
  • 带缓冲区:
1
2
3
4
5
6
7
time dd if=/dev/zero of=test bs=1k count=4096000 && time sync

4096000+0 records in
4096000+0 records out
4194304000 bytes (3.9 G) copied, 40.776918 s, 98 M/s
0m40.79s real 0m01.93s user 0m28.54s system
0m05.11s real 0m00.00s user 0m00.08s system
1
2
Device:            tps    MB_read/s    MB_wrtn/s    MB_read    MB_wrtn
mmcblk0 183.17 0.00 90.60 0 91
  • 不带缓冲区
1
2
3
4
5
6
7
time dd if=/dev/zero of=test bs=1k count=4096000 conv=fsync && time sync

4096000+0 records in
4096000+0 records out
4194304000 bytes (3.9 G) copied, 45.229102 s, 88 M/s
0m45.24s real 0m01.79s user 0m29.42s system
0m00.03s real 0m00.00s user 0m00.01s system
1
2
Device:            tps    MB_read/s    MB_wrtn/s    MB_read    MB_wrtn
mmcblk0 187.00 0.00 92.51 0 92

可见,缓冲区确实有一定的IO性能影响,但是测试过程中dd数据量较大,能够覆盖buffer并触发磁盘flush,因此不论带不带缓冲,两种dd测试都触及了磁盘IO峰值性能。

iostat统计磁盘的实际IO,不受buffer影响

2.2 伸缩IO资源发现对数据库测试成绩影响有限

设计一个实验来降低系统分配给数据库测试工具的IO资源。假设该实验中数据库测试成绩变化有限,可以一定程度地说明数据库CRUD性能测试的瓶颈与IO性能关联不大

通过后台dd占用不同程度的IO资源的方式实现伸缩:

1
2
3
4
5
6
7
8
9
10
11
function command0() {
output=test/test$_depth_
dd if=/dev/zero of=$output bs=1k count=4096
}

function command() {
while (( --_depth_ >= 0 )); do
command0
sleep 0.2
done
}

这将产生一定的IO压力:

1
2
Device:            tps    MB_read/s    MB_wrtn/s    MB_read    MB_wrtn
mmcblk0 102.97 0.00 47.56 0 48

在这套脚本运行时,启动数据库测试,得出跑分成绩仅仅只比空负载时降低了16%左右。如果IO性能是数据库跑分瓶颈的话,那么此时跑分成绩应该显著下降

  • 小结:通过IO加压的方式缩减了能够分配给数据库测试工具的IO资源,但是跑分变化远低于瓶颈预期值。表明IO性能目前不是性能瓶颈

3. CPU资源制约数据库跑分

排除数据库设计、使用方案的影响,也排除掉IO性能的影响,还有一个瓶颈嫌疑是CPU性能。

设计一个实验来伸缩CPU资源。如果跑分对CPU资源的变化很敏感、显著地影响了数据库成绩,那么即可确定瓶颈。

通过削减数据库跑分软件的CPU资源来实现CPU资源的控制,主要采取数据库跑分线程绑定到专用核心,并在该核心上附加一定的压力来实现。

  1. 首先在默认情况下的跑分,可见此时跑分线程会随机地在任意的CPU上运行

数据库测试过程CPU核心信息

注:top1就是数据库跑分线程,top2/3分别是跑分应用的UI线程和渲染线程,它们用于在界面上显示跑分进度条。从数据上看,它们本身也占有一些CPU资源。

  1. 将跑分线程绑定到CPU0~3,将其他线程绑定到4~7

可见,跑分线程只会在CPU0~3中调度。最终测试成绩与默认情况一致,没有差异。

数据库跑分线程0-3

  1. 将跑分线程和其他线程都绑定到CPU7

让跑分线程绑定到CPU7,并因CPU7调度了其他线程而损失一些CPU资源。(本例主要是被跑分工具自己的UI线程和渲染线程分走)

全部绑定到CPU7

可以看到,三个线程全部在CPU7上运行。跑分结果中,成绩较默认情况下降30%,证明该跑分在该设备上是CPU资源敏感型。

  1. 将跑分线程和其他所有线程都绑定到CPU3

与“3”相同,但是绑定CPU3。

成绩较默认下降50%左右。

  • 各场景梳理:
场景(SQLite Delete) 成绩(QPS of SQLite Delete) 说明
默认场景 849
后台dd命令制造峰值IO速率30%左右的IO压力 790 成绩略微下降
允许跑分线程在CPU0-3调度,其他为4-7调度 833 CPU资源几乎没有差异;成绩几乎没有差异
跑分线程和UI线程+渲染线程都绑定到CPU7 532 成绩下降~36%
都绑定到CPU3 392 成绩下降~52%

小结:该机器在数据库跑分场景的性能瓶颈为CPU性能

绑定到不同核心得出的跑分成绩: 大小核性能差异

可以看到绑定到不同CPU会产生比较显著的跑分成绩差异,实际上是CPU的大小核架构中大核和小核之间的性能差异。

cat /proc/cpuinfo可知该机器有两种架构的CPU核。其中CPU part字段代表不同的架构:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
... ... ... ...

processor : 3
BogoMIPS : 26.00
Features : fp asimd evtstrm aes pmull sha1 sha2 crc32 cpuid
CPU implementer : 0x41
CPU architecture: 8
CPU variant : 0x0
CPU part : 0xd03
CPU revision : 4

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

processor : 7
BogoMIPS : 26.00
Features : fp asimd evtstrm aes pmull sha1 sha2 crc32 cpuid
CPU implementer : 0x41
CPU architecture: 8
CPU variant : 0x0
CPU part : 0xd09
CPU revision : 2

查阅Kernel cputype.h可知CPU3为0xd03即小核(Contex-A53),CPU7为0xd09即大核(Contex-A57)。大小核之间的性能差异使之在绑定核心后跑出了不同的分数。

1
2
3
4
5
6
7
#define ARM_CPU_PART_CORTEX_A35         0xD04
#define ARM_CPU_PART_CORTEX_A55 0xD05
#define ARM_CPU_PART_CORTEX_A57 0xD07
#define ARM_CPU_PART_CORTEX_A72 0xD08
#define ARM_CPU_PART_CORTEX_A53 0xD03
#define ARM_CPU_PART_CORTEX_A73 0xD09
#define ARM_CPU_PART_CORTEX_A75 0xD0A

定频带来跑分提升: 强制提高CPU频率

经过测试发现CPU资源下降将显著影响跑分成绩,表明性能瓶颈在CPU。由于在默认情况下CPU在数据库跑分场景可能没有以最高性能运行,因此通过将CPU频率固定到最高频率进行跑分测试。

固定到CPU最高频率运行:

1
2
3
4
5
6
7
find /proc/cpufreq/MT_CPU_DVFS_*/cpufreq_oppidx | xargs cat

[MT_CPU_DVFS_L/4]
cpufreq_oppidx = 0
... ... ... ...
[MT_CPU_DVFS_LL/0]
cpufreq_oppidx = 0
场景(SQLite Delete) 成绩(QPS of SQLite Delete) 说明
将大核、小核均固定到最高频率 1417.1 提升~70%
将大核固定到最高频率 1233.2 提升~48%
将小核固定到最高频率 1103.3 提升~33%
将大核关闭、小核仅开两颗核心并固定到最低频率 253.4 下降~69%

Info:这里为了测试选取固定到最高频率,对于复杂的实际场景,不能笼统地认为最高频率=最佳性能/最好的用户体验。

  • 小结:可以看到,在该数据库测试方法测试情况下,该平台的性能瓶颈为CPU。

结论

对于该设备,AndroBench V5.0.1的数据库性能测试方法中,跑分瓶颈是CPU性能,跑分成绩是CPU敏感的。

优化方案:提升CPU单核性能。

参考

  1. AndroBench