引子
做性能优化的时候,画出火焰图(async-profiler
)来看,发现耗时很多花在了utf-8
的编码解码上了,所以需要思考一下如何优化这部分。
(采样30秒:./profiler.sh -d 30 -f profile.svg [pid]
)
What: 什么是UTF-8编码
UTF-8编码就是对于字节流(一串二进制)的解释,解释成字符串。
类似的还有: ASCII编码,就是把1B字节解释成128种字符。(范围是0-127,包括英文字母、数字)
UTF-8编码下,一个字符可以由1B~4B二进制组成。
UTF-8格式
合法的格式包括4种:
1 | 0xxxxxxx 0-127 (ASCII) |
它的首字节的前缀码会指出这个字符会占用多少字节。
合法的前缀码有4种:
1 | 0X: 占1B; |
显然还有一种10X
不在其中。这是因为10X
不属于合法的首字节的前缀码,只能在第2、3、4个字节中出现。如果我们随机选择utf-8
二进制流中的一个字节来看的话,可能的情况有5种:
1 | 0X: 首字节,这个字符总长1B |
utf-8特性
- 兼容性: 0-127和
ascii
码是一样的,而且在其他字节不可能出现,因此和ascii
完全兼容; - 自同步性: 基于上面讨论的前缀码特性,从任意位置开始读,可以定位到合法位置开始解码。
优化方案
基于上述两点洞察,再考虑到我此次工作只需要解析字母数字部分,再直接分发二进制流即可。因此可以做两点优化:
- 读取时: 不解码,直接理解二进制的每个字节;
- 利用自同步性: 并发解析; 原先只能顺序解码,并发解析可以从任意位置开始;
- 输出时: 不编码,直接输出二进制;
结果
取消编码、解码后这部分耗时下降了12%;
并发处理则大幅提速,提速了一倍以上。