C++ 并行保存矢量到文件

C++ 并行保存矢量到文件,c++,file,join,parallel-processing,concatenation,C++,File,Join,Parallel Processing,Concatenation,我有一个50万个数字的排序向量(在C++中)。将其存储到文本文件大约需要10秒钟,并且只使用50%的CPU(1个内核)。我正在考虑将其并行化,保存两个单独的文件(向量的前半部分和后半部分),然后将这些文件连接起来 问题是,除了逐字节读取并连接到第一个文件之外,我找不到任何不同的连接方式。。。是否有任何独立于平台的方式(Boost或特定于Windows的方式)可以有效地连接文件?尽管如此,您所说的一点似乎强烈表明编写文本文件的方式非常低效。可能您正在使用endl,这会导致刷新。将其替换为\n。其次

我有一个50万个数字的排序向量(在C++中)。将其存储到文本文件大约需要10秒钟,并且只使用50%的CPU(1个内核)。我正在考虑将其并行化,保存两个单独的文件(向量的前半部分和后半部分),然后将这些文件连接起来


问题是,除了逐字节读取并连接到第一个文件之外,我找不到任何不同的连接方式。。。是否有任何独立于平台的方式(Boost或特定于Windows的方式)可以有效地连接文件?

尽管如此,您所说的一点似乎强烈表明编写文本文件的方式非常低效。可能您正在使用
endl
,这会导致
刷新。将其替换为
\n
。其次,如果不能加快速度,考虑一个更有效的文本转换,而不是简单地使用<代码> 连接两个文件可能会花费更多的时间,因为典型的文件系统不支持简单的拼接操作来有效地将多个文件组合在一个文件中。 虽然有一些方法可以使用多核写入文件,但很有可能瓶颈实际上是磁盘IO速度。您可以在Linux系统和许多Unix系统上运行
vmstat1
,查看磁盘写入速度。Windows也有类似的工具,但我永远记不起它的名字。如果您的写入速度接近磁盘速度,则可能无法通过添加更多内核来获得更高的性能

如果您仍想尝试,有三种方法可以奏效:

  • 使用多个线程/进程将向量复制到文件支持的内存映射位置<代码>打开(2)
文件,运行
mmap(2)
将其映射到内存中,然后开始复制数据
  • 使用多线程/进程将数据复制到磁盘,使用
    pwrite(2)
    系统调用指定文件中的偏移量以写入特定的数据块
  • 使用单个线程和
    aio\u write(3)
    系统调用将异步写入提交到磁盘。(我不相信这将实际使用多个内核,但库/内核肯定可以这样实现。)
  • 前两种方法要求您正在编写的数据具有可预测的大小;如果你真的要写500k个数字,它们每个都需要4或8个,或者,让它变得非常简单——只需将前256k个数字分配给第一个线程,然后将下一堆数字分配给下一个线程,从文件中的
    256*1024*8
    字节开始

    编辑


    不要忘记,旋转硬盘驱动器在整个驱动器中查找时会有延迟。线性读写模式最适合旋转金属磁盘。我在前两个要点中建议的随机访问机制,如果每种机制都在写入不同的磁盘(对于单个文件来说很困难:),或者您有一个没有寻道延迟的固态驱动器,则效果最佳。

    我通常同意您的驱动器是瓶颈-但是如果双核系统中的CPU使用率正好为50%,这意味着CPU确实是问题所在。在这种情况下,数字到字符串的转换陷入僵局。请参阅Alf的答案,以获取优化此问题的提示

    并行化,给每个线程一个向量块和一个ostream。第一个线程获取文件作为其ostream,但其他线程获取内存流。第一个线程完成后,当其他线程(按顺序)完成时,将每个内存流写入文件


    格式化现在是并行进行的,对文件的实际写入被序列化。

    格式化非常昂贵。使用fprintf()和fwrite()将128M双精度数字写入磁盘很容易花费10倍的时间,这是因为格式化和大量调用(与一个大fwrite()相比);尝试下面的代码,看看是否有类似的计时。文本文件不是处理大量数据的方式;如果你真的不打算坐下来自己阅读,那么它可能不是ascii格式的

    如果您确实希望保留文本,并且强制使用严格的格式(例如,文件中所有数字的字节数完全相同),则可以将列表分成大的块,让每个核心将一组数字格式化为一个大字符串,并将fseek()设置为文件中的适当位置并将其转储。您可以使用blocksize来查看内存/性能的最佳权衡。如果你真的被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;
    }
    
    #包括
    #包括
    #包括
    #包括
    /*乔纳森·杜西,科学网*/
    #定义文件大小1024*1024*128
    int write_file_bin(常量字符*fname,常量双*数据,常量int数据){
    文件*fp;
    开始、结束的时间;
    fp=fopen(fname,“wb”);
    断言(fp);
    开始=时间(空);
    fwrite(数据,大小(双),数据,fp);
    结束=时间(空);
    fclose(fp);
    返回(int)(结束-开始);
    }
    int write_file_ascii(常量字符*fname,常量双*数据,常量int数据){
    文件*fp;
    开始、结束的时间;
    int i;
    fp=fopen(fname,“wb”);
    断言(fp);
    开始=时间(空);
    
    对于(i=0;i文件不支持这样的操作。操作可能会更慢,因为磁盘必须在两个不同的文件之间连续查找。您是否尝试过对文本文件进行缓冲输出?您的CPU可能与此无关。很可能,此操作的瓶颈是您的硬盘驱动器,而不是CPU。正在写入文本文件磁盘不可避免地是一个线性操作。除非你有两个硬盘,否则没有办法并行。对不起,如果你需要更快的写入速度,你只需要购买一个更快的硬盘。将数据分为两块写入不太可能有帮助;磁盘必须按顺序写入你拥有的所有数据,这是可能的(最终)贝尔特内克