IO 操作密集型程序怎么优化

2022-07-28 16:52:02 +08:00
 killer8998

各位大佬好,我最近接手了一个 native 程序的维护工作(之前以做 Android app 为主),现在遇到一个优化问题,想看看各位有没有什么好的思路。

简单介绍一下这个程序:1.本身是一个单线程程序,定期从一个设备中读取平台下发过来的指令 2.根据指令去操作两类文件,一类是以 ID 命名的文件,一类是汇总了 ID 信息的 bin 文件,举例:有 1-10 个 ID 文件,每个 ID 文件里保存了该文件信息在 bin 文件中的起始位置和长度,而 bin 文件包含了所有 ID 文件的信息。操作主要是新增和删除,新增是创建一个新 ID 文件,然后把相关信息附加到 bin 文件中,删除比较麻烦,需要将 bin 文件读到内存中,然后根据要删除的 ID 文件中的起始位置,做一个 memmove 操作,然后保存到 bin 文件,同时需要修改剩下全部的 ID 文件的起始位置。3.每次新增或者删除了 ID 文件后,都会通知另一个程序来读取剩下的 ID 文件和 bin 文件(可能造成 bin 文件被锁定)

问题:如果平台指令以较慢的速度过来,程序慢慢处理的话 基本上没什么问题,但是如果平台在短时间发送大量指令下来(比如以 20ms 的间隔发送 100 条指令),程序会处于频繁的文件 IO 操作中,然后某些时候 bin 文件读取会有问题,导致后续 memmove 失败,程序崩溃

目前的修改思路:1.平台的指令收到后先放入一个队列,由另一个线程循环读取处理,每次处理有一定的间隔 2.对 bin 文件的操作放在内存中进行,一段时间后没有新指令再写入文件(比如 10s ),同时通知另一个程序来读取变更后的文件。

各位有更好的优化思路的话,欢迎不吝指教,谢谢

936 次点击
所在节点    问与答
4 条回复
Mithril
2022-07-28 17:04:41 +08:00
1. 换 SSD
2. 用内存文件映射操作你那个 bin 文件
3. 用数据库,SQLite 也行,收到 ID 文件内容直接存到数据库里,然后隔段时间从数据库拉数据重新生成 bin

主要问题是,不管是你用队列缓冲指令,还是隔段时间再写入 bin ,长时间把数据保存在内存里风险都会比较大。如果你需要一个可靠的方案保证数据不丢,最好就是每次变更都写入文件。
实际上数据库就是这么干的,所以你可以用它来省点事。
killer8998
2022-07-28 17:08:57 +08:00
@Mithril 老程序。。涉及到和另一个程序的交互。所以不能改文件的组织方式。也就是必须保持现在的 ID 文件和 BIN 文件的方式。。。感觉这种组织方式是不是本身就存在很大的隐患。。
gfreezy
2022-07-28 17:56:47 +08:00
这就是 bitcask 的简易实现。如果使用场景只有根据 ID 获取对应的信息,那删除 ID 的时候可以不实际删除 bin 中的数据,只删除 ID 文件,并把被删除的 ID 记录下来。定期根据被删除的 ID 对应的信息,批量清除 bin 文件数据。这样可以把每次删除一个 ID memmove 一次,合并成删除 n 多次 ID memmove 一次。应该会快不少。
Mithril
2022-07-29 00:08:40 +08:00
@killer8998 没有改啊。
你接受的还是 ID 文件,生成的也还是 bin 。只不过你用一个标准数据库做缓冲,来解决自己实现队列的时候可能会出的各种问题了。
如果你每个 ID 文件的内容,在 BIN 文件中不会重叠在一起,那对于经常会新增和删除的操作来说,链表就是最佳实践了。
你可以看看文件系统是怎么实现的,功能上是类似的。也可以用数据库去实现类似的逻辑,如果想要更安全的话。
用链表来缓存结果,最终隔段时间刷一遍链表生成最终的 BIN 文件就行了。

实际上如果你 ID 文件不是很多,也用不着链表。你就只把所有 ID 文件和内容存下来,然后隔段时间全读取一遍,重新计算生成 BIN 文件就行了。用数据库也只是省得你为了防止脏读自己加锁而已。

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/869279

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX