性能优化题目

26 Feb 2024 | computer architecture, contest

修订历史

  • 2024.02.26 创建笔记
  • 2024.03.15 增加 1BRC
  • 2024.11.28 补充百万队列笔记

DubboMesh

赛题:高 qps 的 dubbo agent 实现

wrk

wrk 是一款针对 http 协议的基准测试工具, 可以使用 Lua 脚本进行复杂测试

其他

百万队列

题目:在单机环境下,实现带有持久化的消息队列引擎。

实现以下接口供生产者进程调用:

实现以下接口供消费者进程调用:

topic和queue是多对多关系。消费者和queue是一一对应关系。发送者可以选择发送到Topic,也可以选择直接发送到Queue。topic和queue的数目大约是100个(其中queue的数目与消费者线程数相等)。

消息要保证发送者内顺序一致。顺序只针对单个topic或者queue,不同topic,不同queue,topic与queue之间都不用考虑顺序。

每条消息大小不会超过256K,总共4kw条。消息由头部(headers)、属性(properties)、主体(body)三个部分组成,其中头部和属性均为KeyValue结构,消息主体为byte[]数组。header与properties中key和value都不会插入null或空值

评测方式:

  1. 10~20个线程各自独立调用Producer,往大约100个Topic发送消息,持续时间T1;
  2. 强行kill Producer进程,未持久化的消息都会丢失
  3. 10~20个线程独立调用Consumer,每个Consumer消费指定的Queue,验证消息顺序准确性,可靠性,消费持续的时间为T2,消费到的总消息数目为N
  4. 以N/(t1+t2)来衡量性能

详细:https://github.com/RapidsBlink/OpenMessageShaping/blob/master/README.md

文件压缩

dirty_ratio和dirty_background_ratio是Linux系统中与磁盘缓存和脏页(dirty pages)相关的参数。

dirty_ratio:该参数定义了系统内存中脏页所占的最大比例。当脏页的比例达到或超过dirty_ratio时,系统会开始触发脏页的写入操作,将其阻塞写入磁盘。该参数的单位是百分比,默认值为20。

dirty_background_ratio:该参数定义了系统内存中脏页所占的比例,达到或超过该比例时,后台进程开始异步写入脏页到磁盘。该参数的单位也是百分比,默认值为10。

综上所述,需要将4kw条数据进行压缩。理论上压缩后的总大小小于物理内存的20%,那磁盘IO对于写线程总是异步的。

此外,每次压缩一条数据,效率比较低。可以考虑设置压缩缓存区

文件读写方式

文件落盘减少锁冲突。

方案:每个名字一个文件夹,例如Topic0或Queue0一个文件夹;在文件夹下面,每个文件是每个线程的名字(因为一个线程绑定一个Producer)

消费架构

多个消费者都订阅同一个topic,需要避免重复读文件,避免重复解码构造并析构消息。

方案:考虑把消费者pull改成push架构:n个文件n个线程来消费文件,分发消息到订阅的消费者队列中;同时若topic文件没被订阅,可以忽略该文件。

存储结构

索引结构

内存读写缓冲区

内存读缓存优化

Linux 内核会将它最近访问过的文件页面缓存在内存中一段时间,这个文件缓存被称为 PageCache。

对象池

数据库同步

题目:进行数据库(从空开始)的主从增量同步,输入为10G顺序append的日志文件(只能单线程顺序读取1次,模拟真实流式处理场景)。日志文件中包含insert key(带有全部属性)/delete key/update property(单个)/update key这四种类型的数据变化操作(通过I/D/U以及其他日志中的字段标识)。其中,10G数据在server端,最后的查询结果需要落盘在client端,server和client是两台配置相同的16逻辑cpu核数的云虚拟机。

详细:https://github.com/RapidsBlink/IncrementalSyncShaping/blob/master/README.md

十亿行数据

https://github.com/gunnarmorling/1brc

题目:10 亿行的文件中,每一行记录的是一个气象站的温度值。气象站和温度分号分隔,温度值只会保留一位小数。解析这个文件,计算出每个气象站的最小、最大和平均温度。按照字典序的格式输出。

优化手段

  1. java 更换 JVM,如 GraalVM
  2. 使用 mmap
  3. java/go 减少中间变量对象的生成,参考 cpp 的 stringview
  4. java HashMap 要求传入 string 对象,为避免创建对象,使用自定义哈希表
  5. java 使用 sun.misc.Unsafe 而不是 MemorySegment,来避免边界检查
  6. 位运算替代分支、simd
  7. 数据细分成大量小块,避免某些线程提前计算完成
  8. cpp 自定义 allocator,减少 malloc 次数
  9. go channel 批量数据传输,减少锁争用
  10. go 复用对象,减少内存分配
  11. go 考虑分块读取文件

其他

参考


Older · View Archive (39)

Haskell 类型系统进阶

Newer

一个作业评测系统的全栈实现