C++ 并行化文件 I/O 并使用 OpenMP 任务进行分析
C++ Parallelising file I/O and analysis with OpenMP tasks
我目前正在一个系统上工作,我在其中读取超过 2 亿条记录(行)的文件,所以我认为我可以使用生产者-消费者模型来提高性能(在我阅读时工作)。但是,我没有实现强大的性能并且担心我的总体设计是错误的。放在上下文中:
int i = 0;
string buffer[MAX_SIZE];
//critical regions exist for map_a and map_b (shared below) in the task function
#pragma omp parallel shared(map_a), shared(map_b), num_threads(X)
#pragma omp single
{
while (getline(fin, line) && !fin.eof())
{
buffer[i] = line;
if (++i == MAX_SIZE)
{
#pragma omp task firstprivate(buffer)
work_on_data(buffer, map_a, map_b);
i = 0;
}
}
}
缓冲区中的每条记录在 work_on_data
中大约需要 49-95μ 的时间来处理,由于条件的差异,我怀疑 pragma omp critical
区域(每个共享地图一个)。对于两个关键区域:
- 对于map_a:如果根据记录,某个案例成立,则需要使用从记录派生的键将条目添加到映射中。如果条目已存在,则需要对其进行更新。在映射读取、潜在更新和写入上有一个临界区。
- 对于map_b:对于每条记录,必须更新地图。临界区涵盖与 (1) 相同的操作,即读取、潜在 update/insertion 和写入。
所以,关于我的方法。我应该使用单独的 pthread 来缓冲 IO 吗?我是否应该简单地缓冲到一个巨大的内存分配缓冲区中并创建 pragma omp parallel for
对其记录子集的任务?我对这种编程没有经验。
提前致谢!
编辑: 澄清临界区的使用。
关于 IO,我认为您不会获得太多性能,因为它应该已经被 OS 很好地缓冲了。您总是可以尝试自己实现大缓冲(可能使用 producer/consumer),或使用内存映射文件,但恐怕您会对性能提升感到失望(而且 getline 简单得多)。
关于文件分析,你当然应该尝试优化计算本身,但如果你能去除关键区域,可能会有更好的收益。通常,目标是完全消除对共享对象的依赖。你如何做取决于你的应用程序,但一般的想法是在每个线程中进行独立处理,然后将结果合并在一起。在您的情况下,您可以在每个线程中分配独立的地图,然后再更新真实地图。如果您需要原始地图进行处理,请阅读它们但不要 update/write 它们,编写独立的对象并在以后更新。这样你就可以删除关键区域(读取操作是线程安全的)。
附带说明一下,这是非常特定于应用程序的,也是特定于硬件的。如果您的处理时间比文件读取时间短(这在很大程度上取决于您的 CPU/HDD/SSD),您可能会通过更好的 IO 缓冲获得更多性能,甚至可能使多线程变得无用。此外,如果结果合并太重,拆分结果可能不值得。你如何 split/merge 结果很重要;您可以只构建一个要执行的更新列表,或者构建一个您将合并的实际地图。关键区域也可能没有问题。尝试进行实验,看看哪种方法更适合您。
我目前正在一个系统上工作,我在其中读取超过 2 亿条记录(行)的文件,所以我认为我可以使用生产者-消费者模型来提高性能(在我阅读时工作)。但是,我没有实现强大的性能并且担心我的总体设计是错误的。放在上下文中:
int i = 0;
string buffer[MAX_SIZE];
//critical regions exist for map_a and map_b (shared below) in the task function
#pragma omp parallel shared(map_a), shared(map_b), num_threads(X)
#pragma omp single
{
while (getline(fin, line) && !fin.eof())
{
buffer[i] = line;
if (++i == MAX_SIZE)
{
#pragma omp task firstprivate(buffer)
work_on_data(buffer, map_a, map_b);
i = 0;
}
}
}
缓冲区中的每条记录在 work_on_data
中大约需要 49-95μ 的时间来处理,由于条件的差异,我怀疑 pragma omp critical
区域(每个共享地图一个)。对于两个关键区域:
- 对于map_a:如果根据记录,某个案例成立,则需要使用从记录派生的键将条目添加到映射中。如果条目已存在,则需要对其进行更新。在映射读取、潜在更新和写入上有一个临界区。
- 对于map_b:对于每条记录,必须更新地图。临界区涵盖与 (1) 相同的操作,即读取、潜在 update/insertion 和写入。
所以,关于我的方法。我应该使用单独的 pthread 来缓冲 IO 吗?我是否应该简单地缓冲到一个巨大的内存分配缓冲区中并创建 pragma omp parallel for
对其记录子集的任务?我对这种编程没有经验。
提前致谢!
编辑: 澄清临界区的使用。
关于 IO,我认为您不会获得太多性能,因为它应该已经被 OS 很好地缓冲了。您总是可以尝试自己实现大缓冲(可能使用 producer/consumer),或使用内存映射文件,但恐怕您会对性能提升感到失望(而且 getline 简单得多)。
关于文件分析,你当然应该尝试优化计算本身,但如果你能去除关键区域,可能会有更好的收益。通常,目标是完全消除对共享对象的依赖。你如何做取决于你的应用程序,但一般的想法是在每个线程中进行独立处理,然后将结果合并在一起。在您的情况下,您可以在每个线程中分配独立的地图,然后再更新真实地图。如果您需要原始地图进行处理,请阅读它们但不要 update/write 它们,编写独立的对象并在以后更新。这样你就可以删除关键区域(读取操作是线程安全的)。
附带说明一下,这是非常特定于应用程序的,也是特定于硬件的。如果您的处理时间比文件读取时间短(这在很大程度上取决于您的 CPU/HDD/SSD),您可能会通过更好的 IO 缓冲获得更多性能,甚至可能使多线程变得无用。此外,如果结果合并太重,拆分结果可能不值得。你如何 split/merge 结果很重要;您可以只构建一个要执行的更新列表,或者构建一个您将合并的实际地图。关键区域也可能没有问题。尝试进行实验,看看哪种方法更适合您。