多线程应用程序中最快的文件读取方式

10

我需要将一个8192x8192的矩阵读入内存。我希望尽可能快地完成这个任务。
目前我有以下结构:

char inputFile[8192][8192*4]; // I know the numbers are at max 3 digits
int8_t matrix[8192][8192]; // Matrix to be populated

// Read entire file line by line using fgets
while (fgets (inputFile[lineNum++], MAXCOLS, fp));

//Populate the matrix in parallel, 
for (t = 0; t < NUM_THREADS; t++){
    pthread_create(&threads[t], NULL, ParallelRead, (void *)t);
}

在函数ParallelRead中,我解析每一行,执行atoi并填充矩阵。并行性是按行进行的,例如线程t解析行t,t + 1 * NUM_THREADS..

在一个有两个核心和2个线程的系统上,这需要。

Loading big file (fgets) : 5.79126
Preprocessing data (Parallel Read) : 4.44083

有没有更进一步优化的方法?

3
可以考虑在有足够数据可用的情况下,同时开始填充线程和进行输入/输出操作。 - vanza
@AlanStokes,如果我有足够的资源,我认为节省4.44秒没有任何问题。实际上,在一台拥有40个核心的机器上,我成功将预处理时间缩短到了0.1秒。 - sud03r
@vanza 你可能是对的,这也是我在考虑的事情。我可以让fgets()在一个线程中继续运行,并在另一个线程中开始预处理任务,但这将需要我的线程等待数据是否可用,而且我已经看到了性能惩罚。 - sud03r
2
我所知道的提高磁盘读取性能的方法只有三种:1)从压缩源读取数据;2)使用更快的磁盘或RAID阵列;3)将数据分割到不同的磁盘上,每个磁盘读取一个线程。通常情况下,如果单个线程无法跟上磁盘读取时间,那么你就会遇到大问题。 - mfa
1
将数据存储为二进制。如果每个矩阵元素最多可以采用256个不同的值,那么我们在这里需要处理64MB的数据,这应该是现代硬件可以轻松处理的。然后您还可以将文件直接内存映射到程序中。 - Kerrek SB
显示剩余5条评论
4个回答

34

这样做是一个不好的想法。如果你有足够的核心但仍只有一个硬盘,线程可以获得更多的CPU周期,但无法改善读取文件数据的速度。

实际上,它会使情况变得更糟。从文件中读取数据最快的方式是按顺序访问文件。这最大限度地减少了读取器头寻道的数量,这是磁盘驱动器上最昂贵的操作。通过将读取跨多个线程分割,每个线程读取文件的不同部分,您使读取器头不断地来回跳动。非常、非常有损于吞吐量。

使用仅一个线程来读取文件数据。您可能能够通过在加载文件数据块后启动线程,将其与一些计算周期重叠来提高效率。

请注意测试效果。当您重新运行程序时,通常在微调代码之后,程序可能会发现文件数据存储在文件系统缓存中,因此不必从磁盘中读取。这非常快,内存总线速度,即内存到内存的复制。在您的数据集上非常可能,因为它不是很大并且容易适应现代机器所拥有的RAM量。这在生产机器上(通常)不会发生。因此,请确保清除缓存以获得真实的数字,无论您的操作系统需要什么操作。


2
他并没有并行读取文件,而是在内存中将字符串转换为int8_t类型的数据。这样做没有任何问题。 - kratenko
1
我从未声称这有任何问题。事实上,我建议将其与读取数据的线程重叠。 - Hans Passant

2
值得考虑的一件事是分配两个较小的输入缓冲区(例如,每个缓冲区有200行)。
然后让一个线程读取数据到输入缓冲区。 当一个输入缓冲区已满时,将其传递给第二个线程进行解析。这个第二个线程可以使用线程池来进行并发解析(请检查OpenMP)。
你必须使用锁/互斥来确保任何一个线程都具有独占访问权限。
这种方法更好,因为解析现在与读取文件并发进行,而且你对缓冲区的内存访问更加本地化,并且将适合于CPU缓存。这可以提高读取和解析速度。
如果fgets是瓶颈,也可以将文件作为二进制数据读入内存。这可能会提高读取速度,但需要你进行额外的解析,并使上述优化更难实现。

2

尝试使用类似 fread 的方法,通过父进程加载字符数组,将所有内容一次性读取为一个大字符串。

让父进程遍历字符串,找到第一行或基于大小计算出第一行的位置。将该行的处理交给一个线程完成,然后继续处理下一行,直到文件结束。与线程同步,完成。


2

使用内存映射可以获得最佳的文件I/O性能。这是一个例子。我建议从单线程设计开始,如果后续处理证明成为瓶颈,则将其改为并行处理。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接