有比fseek和fwrite更快的写入方法吗?

5

我有一个1GB的二进制文件,基本上包含相同类型值的3D立方体。使用不同顺序([x,y,z]或[z x, y])保存这种立方体需要使用fseek和fwrite花费很长时间。但是其中一个软件包比我的程序快得多。有没有方法可以使文件写入速度比使用fseek/fwrite更快?


5
也许它按顺序写入文件,跳跃式地访问内存,而不是按顺序访问内存并跳跃文件。也许它将文件映射到内存中,并因此获得“更好”的I/O(通过不必每次寻址都刷新)。也许它使用更好的缓冲区大小。简而言之:你的代码可能存在很多次优的方式,而这个程序可以利用这些方式来击败你。 - Steve Jessop
就写入速度而言,很难超越fwrite。也许使用write可以节省一两微秒的时间,但是你无法从“慢”变成“快得多”。然而,我肯定会避免使用fseek - Sergey Kalinichenko
@SteveJessop 谢谢您的评论。它们是非常好的观点。但第一个观点不可能实现,因为内存中的缓冲区也从不同的立方体文件读取 - 所以我必须在任一文件中使用寻址。至于缓冲区大小,我会尝试增加并寻找内存映射方法。 - Tae-Sung Shin
1
@david:我不认为这是正确的:你可以顺序读取一个文件,然后在内存中转置立方体,然后顺序写入另一个文件。你只需要1或2 GB的RAM。毫无疑问,有些系统的磁盘容量可能只有1GB,但没有1GB的RAM,但如果你使用这样的系统,那就真的很倒霉了,因为这意味着你已经陷入了平台开发时间的一个相当窄的窗口中;-) 我敢打赌是某种手机。 - Steve Jessop
@SteveJessop 我尝试将所有内容都放在内存中,但这并没有起作用——最大限制为500MB,因为程序还需要使用内存来运行其它部分。由于我有6GB的内存,这个限制可能是因为程序是用VC6 32位编写的。 - Tae-Sung Shin
3个回答

7
在文件io操作的内部循环中,不应使用fseek。为了使写入函数更快,它们会缓存写入。如果你到处寻找,就会一直清空缓存。
在内存中进行所有的转换-例如,在内存中旋转立方体,然后通过几个连续的fwrite调用将文件写入。
如果你无法完全在内存中转换数据,则在内存中逐层组装立方体并写出每个层面。
@编辑:
在你的情况下,根本不需要使用fseek。一个都不要用。
做这样的事情:
void writeCubeZYX( int* cubeXYZ, int sizeOfCubeXYZ, FILE* file )
{
   int* cubeZYX = malloc( sizeOfCubeXYZ );

   // all that monkey business you're doing with fseek is done inside this
   // function copying memory to memory. No file IO operations in here.
   transformCubeXYZ_to_ZYX( cubeXYZ, cubeZYX, sizeOfCubeXYZ );

   // one big fat very fast fwrite. Optimal use of file io cache.
   fwrite(  file, cubeZYX, 1, sizeOfCubeXYZ );

   free( cubeZYX ); // quiet pedantry.
}

@edit2:

假设您无法将所有内容都转换为内存中,则可以将其转换为平面,一次写出一个平面 - 按文件顺序 - 即不使用fseek。

因此,假设一个[XYZ]立方体在内存中被布置为一系列Z [XY]矩阵。也就是说,您的立方体的[XY]平面在内存中是连续的。并且您想要以[ZYX]的形式写出。因此在文件中,您要写出一系列X [ZY]矩阵。每个[ZY]在文件中是连续的。

因此,您可以像这样操作:

void writeCubeZYX( int* cubeXYZ, int x, int y, int z, FILE* file )
{
   int sizeOfPlaneZY = sizeof( int ) * y * z; 
   int* planeZY = malloc( sizeOfPlaneZY );

   for ( int i = 0; i < X; i++ )
   {
      // all that monkey business you're doing with fseek is done inside this
      // function extracting one ZY plane at a time. No file IO operations in here.
      extractZYPlane_form_CubeXYZ( cubeXYZ, planeZY, i );

      // in X big fat very fast fwrites. Near optimal use of file io cache.
      fwrite(  file, planeZY, 1, sizeOfPlaneZY );
   } 

   free( planeZY ); // quiet pedantry.
}    

你的最后一个建议正是我正在做的。但是考虑从[x,y,z]转换为[z,x,y]的立方体。使用一个平面,你需要nxnynz次搜索。如果这不正确,请告诉我。 - Tae-Sung Shin
好的,我不知道你是怎么决定我可以把所有东西都放在内存中的,但那并不是真的。猴子的事情应该做完了,因为可能会有更大尺寸的立方体,比如4GB甚至10GB。如果我可以把所有东西都放在内存中,... - Tae-Sung Shin
在我的情况下,cubeXYZ 也没有在内存中。但无论如何,我理解你的观点并知道现在该怎么做——使用最大尺寸的缓冲区将是答案。感谢您在此过程中的耐心。 - Tae-Sung Shin

1
如果你需要进行大量的随机访问写入操作,建议使用mmap。mmap将内存页映射到文件中,并由操作系统控制,类似于内存交换机制。
另一种方法是可以使用异步IO。它由GLIBC提供。http://www.gnu.org/software/libc/manual/html_node/Asynchronous-I_002fO.html 它简单地将数据放入内存队列中,然后创建另一个线程来管理IO。

在Linux上是个好主意,但或许在Windows上mmap不可用,不过可能有一些等效的方法。 - Basile Starynkevitch

0

如果您不介意将磁盘上的文件作为压缩文件,则在写入时进行压缩可能会更快。这样做可以加快速度,因为瓶颈通常是将字节写入磁盘,通过在写入时进行压缩,可以减少需要写入的字节数。

当然,这取决于您的数据是否适合压缩。在c++中压缩输出的一种选择是gzip。例如:如何读取/写入gzipped文件?

但在您的情况下,这可能不适用——从您的问题中不清楚您何时/为什么进行fseek。您预期的写入模式是什么?


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接