将矢量并行保存到文件
Posted
技术标签:
【中文标题】将矢量并行保存到文件【英文标题】:Saving vector to file in parallel 【发布时间】:2011-09-25 00:42:49 【问题描述】:我有一个包含 50 万个数字的排序向量(在 C++ 中)。将其存储到文本文件大约需要 10 秒,并且仅使用 50% 的 CPU(1 个核心)。我正在考虑并行化它,保存 2 个单独的文件(矢量的前半部分和后半部分),然后连接这些文件。
问题是,除了逐字节读取并加入第一个文件之外,我无法找到任何不同的连接方式...是否有任何独立于平台的方式(Boost 或 Windows 特定)有效地加入文件?
【问题讨论】:
文件不支持这样的操作。 操作可能会更慢,因为磁盘必须在两个不同的文件之间不断寻找。 你试过缓冲输出到文本文件吗? 你的 CPU 在这里可能无关紧要。很可能,此操作的瓶颈是您的硬盘驱动器,而不是 CPU。写入磁盘不可避免地是线性操作。除非您有两个硬盘驱动器,否则无法并行化它。对不起。如果您需要更快地写入,您只需购买速度更快的硬盘即可。 将数据分成 2 块写入不太可能有帮助;磁盘必须按顺序写入您拥有的所有数据,这可能是(最终)瓶颈。您是否考虑过以本机二进制格式写入数据,例如,为这些浮点数写入二进制值而不是相应的文本?这避免了很多格式化开销,并且可能会更小并且相应地更快。您还应该考虑使用内存映射 I/O 工具来避免所有标准缓冲废话和正常顺序 I/O 的开销。你可以用同样的方法读回数据。 【参考方案1】:尽管如此,您所讲的内容似乎强烈表明编写文本文件的效率非常低。可能您正在使用endl
,这会导致flush
。将其替换为 \n
。接下来,如果这不能加快速度,请考虑比简单地使用<<
更有效的数字到文本转换。 sprintf
浮现在脑海中。最后,如果您仍在 10 秒范围内而不是 1/10 秒范围内,请考虑更严格的优化(例如,在 Windows 机器上,您可能会在开始时分配具有正确大小的文件,等等)。
干杯,
【讨论】:
+1 作为一个简单的测试,使用for ( int i=0; i<v.size(); i++ ) myfile << v[i] << "\n";
在我的 Athlon 64 2,7GHz 上运行 0.3 秒。可能“endl”是一个原因。
第一个问题是endl,第二个是VS中的“debug”模式——“release”编译后,程序运行速度快3-4倍……【参考方案2】:
连接两个文件可能会花费更多时间,因为典型的文件系统不支持简单的拼接操作来有效地将多个文件拼凑成一个文件。
虽然有一些方法可以使用多核写入文件,但很有可能非常瓶颈实际上是您的磁盘 IO 速度。您可以在 Linux 系统和许多 Unix 系统上运行 vmstat 1
来查看您的磁盘写入速度。 (以及许多其他巧妙的措施。)Windows 有一个类似的工具,但我永远记不起它的名字。如果您的写入速度接近磁盘的速度,则可能无法通过添加更多内核来获得更高的性能。
如果您仍然想尝试,可以使用三种方法:
使用多个线程/进程从向量复制到文件支持的内存映射位置。open(2)
文件,运行mmap(2)
将其映射到内存中,然后开始复制数据。
使用多个线程/进程将数据复制到磁盘,使用pwrite(2)
系统调用指定文件中的偏移量以写入该特定数据块
使用单线程和aio_write(3)
系统调用将异步写入提交到磁盘。 (我不相信这实际上会使用多个内核,但库/内核肯定可以以这种方式实现它。)
前两种方法要求您写入的数据是可预测的大小;如果你真的要写 500k 个数字,它们每个都需要 4 或 8 或 some other fixed size,这很容易——只需将前 256k 个数字分配给第一个线程,然后将下一堆数字分配给下一个线程,从256*1024*8
字节开始到文件中。
编辑
不要忘记旋转硬盘驱动器在搜索整个驱动器时会有延迟。线性读写模式最适合旋转金属磁盘。我在前两个要点中建议的随机访问机制如果每个都写入不同的磁盘(单个文件很难:)或者你有一个没有寻道延迟的固态驱动器,则效果最好。
【讨论】:
【参考方案3】:我通常同意您的驱动器是瓶颈 - 但如果 CPU 使用率在双核系统中恰好 50%,这意味着 CPU 确实是问题所在。在这种情况下,陷入困境的是数字到字符串的转换。有关优化此问题的提示,请参阅 Alf 的回答。
并行化,为每个线程提供一个向量块和一个 ostream。第一个线程获取文件作为其 ostream,但其他线程获取内存流。一旦第一个线程完成,并且随着其他线程完成(按顺序),将每个内存流写入文件。
格式化现在是并行完成的,实际写入文件被序列化。
【讨论】:
格式化应该比磁盘写入快,IMO。也许有更有效的格式化程序?例如,他可以使用 stdio.h 而不是 iostream。如果他打算定期处理如此大量的数据,他可能会通过滚动自己的类似 sprintf 的函数来提高效率。 @jforberg:是的 - 我同意格式化应该更快。不过,我发现 50% 的 CPU 使用率非常可疑。它通常表示双核系统上的一个核心 100% 繁忙。【参考方案4】:格式化非常昂贵。使用 fprintf() 与 fwrite() 将 128M 双精度数字写入磁盘很容易花费 10 倍的时间,因为格式化和大量调用(与一个大 fwrite() 相比);试试下面的代码,看看你是否得到类似的时间。文本文件不是处理大量数据的方法;如果您实际上不打算自己坐下来阅读它,那么它就不会是 ascii。
如果您确实想要保留文本,并且强制采用严格的格式(例如,所有数字在文件中占用完全相同的字节数),那么您可以拆分列表分成大块,并让每个核心将一组数字格式化为一个大字符串,并将 fseek() 格式化为文件中的适当位置并将其转储。您可以使用块大小来查看内存/性能的最佳权衡是什么。如果您确实受到 CPU 的限制,这应该允许您将 I/O 与计算重叠并获得一些胜利。
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <time.h>
/* Jonathan Dursi, SciNet */
#define FILESIZE 1024*1024*128
int write_file_bin(const char *fname, const double *data, const int ndata)
FILE *fp;
time_t start, end;
fp=fopen(fname,"wb");
assert(fp);
start = time(NULL);
fwrite(data, sizeof(double), ndata, fp);
end = time(NULL);
fclose(fp);
return (int)(end-start);
int write_file_ascii(const char *fname, const double *data, const int ndata)
FILE *fp;
time_t start, end;
int i;
fp=fopen(fname,"wb");
assert(fp);
start = time(NULL);
for (i=0;i<ndata;i++)
fprintf(fp,"%lf\n",data[i]);
end = time(NULL);
fclose(fp);
return (int)(end-start);
int main(int argc, char **argv)
double *data;
int i;
int asciitime, bintime;
data = (double *)malloc(FILESIZE * sizeof(double));
assert(data);
for (i=0;i<FILESIZE;i++)
data[i] = i*(double)i/2.;
asciitime = write_file_ascii("data.txt",data,FILESIZE);
bintime = write_file_bin("data.dat",data,FILESIZE);
printf("Time to write files: ASCII: %d, Binary: %d\n",asciitime, bintime);
return 0;
【讨论】:
以上是关于将矢量并行保存到文件的主要内容,如果未能解决你的问题,请参考以下文章