如何在 C++ 中并发写入文件(换句话说,写入文件的最快方法是什么)

How to concurrently write to a file in c++(in other words, whats the fastest way to write to a file)

我正在构建图形引擎,我需要将结果图像写入 .bmp 文件。我将像素存储在 vector<Color> 中。同时还保存图像的宽度和高度。目前我写的图片如下(这段代码不是我自己写的):

std::ostream &img::operator<<(std::ostream &out, EasyImage const &image) {

//temporaryily enable exceptions on output stream
enable_exceptions(out, std::ios::badbit | std::ios::failbit);
//declare some struct-vars we're going to need:
bmpfile_magic magic;
bmpfile_header file_header;
bmp_header header;
uint8_t padding[] =
        {0, 0, 0, 0};
//calculate the total size of the pixel data
unsigned int line_width = image.get_width() * 3; //3 bytes per pixel
unsigned int line_padding = 0;
if (line_width % 4 != 0) {
    line_padding = 4 - (line_width % 4);
}
//lines must be aligned to a multiple of 4 bytes
line_width += line_padding;
unsigned int pixel_size = image.get_height() * line_width;

//start filling the headers
magic.magic[0] = 'B';
magic.magic[1] = 'M';

file_header.file_size = to_little_endian(pixel_size + sizeof(file_header) + sizeof(header) + sizeof(magic));
file_header.bmp_offset = to_little_endian(sizeof(file_header) + sizeof(header) + sizeof(magic));
file_header.reserved_1 = 0;
file_header.reserved_2 = 0;
header.header_size = to_little_endian(sizeof(header));
header.width = to_little_endian(image.get_width());
header.height = to_little_endian(image.get_height());
header.nplanes = to_little_endian(1);
header.bits_per_pixel = to_little_endian(24);//3bytes or 24 bits per pixel
header.compress_type = 0; //no compression
header.pixel_size = pixel_size;
header.hres = to_little_endian(11811); //11811 pixels/meter or 300dpi
header.vres = to_little_endian(11811); //11811 pixels/meter or 300dpi
header.ncolors = 0; //no color palette
header.nimpcolors = 0;//no important colors

//okay that should be all the header stuff: let's write it to the stream
out.write((char *) &magic, sizeof(magic));
out.write((char *) &file_header, sizeof(file_header));
out.write((char *) &header, sizeof(header));

//okay let's write the pixels themselves:
//they are arranged left->right, bottom->top, b,g,r
// this is the main bottleneck
for (unsigned int i = 0; i < image.get_height(); i++) {
    //loop over all lines
    for (unsigned int j = 0; j < image.get_width(); j++) {
        //loop over all pixels in a line
        //we cast &color to char*. since the color fields are ordered blue,green,red they should be written automatically
        //in the right order
        out.write((char *) &image(j, i), 3 * sizeof(uint8_t));
    }
    if (line_padding > 0)
        out.write((char *) padding, line_padding);
}
//okay we should be done
return out;
}

如你所见,像素正在被一个一个地写入。这个比较慢,我在程序里放了一些定时器,发现写是我的主要瓶颈。

我试图写完整(水平)行,但我没有找到如何去做(我找到的最好的是 this

其次,我想使用多线程写入文件(不确定是否需要使用线程或处理)。使用 openMP。但这意味着我需要指定写入哪个字节地址,我认为这是我无法解决的问题。

最后,我想过每当我绘制一个对象时立即写入文件,但后来我在写入文件中的特定位置时遇到了同样的问题。

因此,我的问题是:解决此问题的最佳(最快)方法是什么。 (为 windows 和 linux 编译)

写入文件最快的方法是使用硬件辅助。将你的输出写入内存(a.k.a.buffer),然后告诉硬件设备从内存传输到文件(磁盘)。

下一个最快的方法是将所有数据写入缓冲区,然后将数据分块写入文件。如果您希望其他任务或线程在您的写入过程中执行,则创建一个将缓冲区写入文件的线程。

写入文件时,每次事务的数据越多,写入效率就越高。例如,1024 字节的 1 次写入比 1 字节的 1024 次写入更快。

想法是保持数据流。放慢传输速率可能比突发写入、延迟、突发写入、延迟等更快

记住磁盘本质上是一个串行设备(除非你有一个特殊的硬盘)。使用位流将位放置在盘片上。并行写入数据会产生不利影响,因为磁头必须在并行活动之间移动。

请记住,如果您使用多个核心,数据总线上的流量就会增加。当其他 threads/tasks 正在使用数据总线时,到文件的传输将不得不暂停。因此,如果可以,请阻止所有任务,然后传输您的数据。 :-)

我编写过从慢速内存复制到快速内存,然后从快速内存传输到硬盘驱动器的程序。那也是在使用中断(线程)。

总结
快速写入文件涉及:

  1. 保持数据流;尽量减少停顿。
  2. 以二进制模式写入(请勿翻译)。
  3. 分块写入(在写入块之前根据需要格式化到内存中)。
  4. 最大化交易中的数据。
  5. 如果您希望其他任务运行“并发”,请使用单独的写入线程。
  6. 硬盘是串行设备,不是并行的。位以串行流的形式写入盘片。