我的应用程序使用文本文件将数据存储到文件中。 我正在测试通过多线程操作来读取它的最快方法。 我使用了以下两种技术:
使用与NUMBER_OF_PROCESSORS个环境变量一样多的流。每个流都在不同的线程上。为每个流平均分配文件中的行总数。解析文本。
只有一个流解析整个文件并将数据加载到内存中。创建线程(= NUMBER_OF_PROCESSORS - 1)来解析内存中的数据。
测试在各种文件大小100kB - 800MB上运行。 文件中的数据:
100.23123 -42343.342555 ...(and so on)
4928340 -93240.2 349 ...
...
数据存储在double
的二维数组中。
结果:两种方法在解析文件时花费大致相同的时间。
问题:我应该选择哪种方法?
方法1对硬盘不利,因为多个读取访问同时在随机位置执行。
方法2很糟糕,因为所需的内存与文件大小成正比。这可以通过将容器限制为固定大小,删除已解析的内容并从阅读器再次填充来部分克服。但这会增加处理时间。
答案 0 :(得分:4)
方法2有一个顺序瓶颈(单线程读取和分发工作项)。根据Amdahls Law,这不会无限缩放。不过,这是一种非常公平和可靠的方法。
方法1没有瓶颈并且会扩展。确保不会在磁盘上造成随机IO。我使用互斥锁一次只能读取一个线程。读入可能为4-16MB的大顺序块。在磁盘执行单头搜索时,它可以读取大约1MB的数据。
如果解析这些行需要相当长的时间,则由于序列部分较大,因此无法使用方法2。它不会扩展。但是,如果解析速度很快,请使用方法2,因为它更容易正确。
为了说明瓶颈的概念:想象一下1.000.000计算线程要求一个读者线程给它们行。那个读者线程无法像他们要求的那样快速地分发线路。您不会获得吞吐量的1e6倍。这不会扩展。但是,如果1e6线程独立于非常快的IO设备读取,那么吞吐量将达到1e6倍,因为没有瓶颈。 (我使用了极端数字来表达观点。同样的想法适用于小型。)
答案 1 :(得分:1)
我更喜欢略微修改的2方法。我会通过大块的单个线程连续读取数据。就绪块传递给处理数据的线程池。所以你有阅读和阅读处理
答案 2 :(得分:0)
如果有足够的RAM,您可以在没有单线程瓶颈的情况下完成。对于Linux:
1)使用MAP_LOCKED将整个文件映射到RAM,需要root或系统范围的权限调整。或者没有用于SSD的MAP_LOCKED,它们可以很好地处理随机访问。
2)给每个线程一个开始位置。处理从自行开始位置后的第一个换行到下一个螺纹开始位置后的第一个换行的数据。
PS你的程序CPU负载是多少?可能硬盘是瓶颈。