8

我必须将 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

有没有办法进一步优化这个?

4

4 回答 4

30

这样做是个坏主意。如果您有足够的内核但您仍然只有一个硬盘,线程可以获得更多的 CPU 周期。所以难免线程无法提高读取文件数据的速度。

他们实际上使情况变得更糟。当您按顺序访问文件时,从文件中读取数据的速度最快。这最大限度地减少了读取器磁头寻道的次数,这是迄今为止磁盘驱动器上最昂贵的操作。通过将读取拆分到多个线程中,每个线程读取文件的不同部分,您正在使阅读器的头部不断地来回跳跃。对吞吐量非常非常不利。

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

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

于 2012-05-20T20:49:19.107 回答
2

尝试使用诸如 fread 之类的加载字符数组的父线程将 1 io 中的所有内容加载为一个很棒的大字符串。

让父母遍历字符串,找到 1 行,或根据大小计算第一行的位置。将该行的处理交给一个线程。下一行,冲洗,重复,直到 EOF。与线程同步。完毕。

于 2012-05-20T20:30:18.453 回答
2

值得考虑的一件事是分配两个较小的输入缓冲区(假设它们每个将是 200 行)。

然后让一个线程将数据读入输入缓冲区。当一个输入缓冲区已满时,将其传递给执行解析的第二个线程。第二个线程可以使用线程池进行并发解析(检查 openMP)。

您将不得不使用锁/互斥锁来确保任一线程具有独占访问权限。

这会更好,因为现在解析与读取文件是并发的,并且您对缓冲区的内存访问更加本地化并且适合您的 CPU 缓存。这可以提高读取和解析速度。

如果 fgets 是瓶颈,您还可以将文件作为二进制文件读入内存。这可以提高读取速度,但需要您进行额外的解析,并使上述优化更难执行。

于 2012-05-20T19:58:54.780 回答
2

使用文件 I/O 可以获得的最佳性能是通过内存映射。这是一个例子。我将从单线程设计开始,如果后加载处理被证明是一个瓶颈,请使其并行。

于 2012-05-20T20:33:15.060 回答