2

所以我正在阅读相对较大的文件(> = 1GB),其中包含数百万条记录,每条记录都属于一个特定的组。有100组。为了更有效地处理数据,我创建了 100 个文件,每组 1 个。(在追加模式下使用 fopen。)当我从大文件中读取记录时,我将每个记录写入相应的小文件。我保持所有文件的文件指针始终打开,这样我就不会打开和关闭每条记录的文件。

这需要非常长的时间,并且读入(和写)的速度不是恒定的。它开始很快,然后会慢慢爬行,然后再次加速,然后缓慢。读取的文件越多,情况似乎越糟。

关于正在发生的事情的一种可能性是,随着它们变得越来越大,较小的文件需要重新定位到存储中。这将是令人惊讶的,因为我有 47GB 的空闲空间(约 500 个)。但我想不出别的。我会看看重新碎片是否有帮助,但与此同时,有人知道发生了什么以及如何解决这个问题吗?有没有办法预先指定要创建的文件的大小,类似于std::vector::reserve

4

4 回答 4

2

您只是看到文件系统缓存填满容量的副作用,然后必须等到空间被实际写入磁盘的数据释放。这是冰川缓慢的。虽然缓存中有空间,但 write() 调用会执行内存到内存的复制,以每秒 5 GB 或更高的速度运行。磁盘写入速度很少优于 30 兆字节/秒。巨大的差异,当缓存已满时,您正在测量磁盘写入速度。

您将需要更多 RAM 或更快的磁盘。

于 2012-12-14T11:28:16.007 回答
2

如果您无法或不愿意重组程序以一次写入一组,请为每个“小”文件(使用setbuf, setvbuf)设置更大的缓冲区。这样做的效果是缓冲区刷新到磁盘将表现出更多的“局部性”,即不是将 X 量的数据刷新 100 次到 100 个不同的文件,而是将 10 倍的数据量刷新到 100 个不同的文件 10 次。

测试用例程序(故意没有错误检查):

-- hugefile.h --

struct record
{
  unsigned int group;
  char data [1020];
};


--- gen-hugefile.c ---

#include <stdio.h>
#include <stdlib.h>

#include "hugefile.h"

int
main (int argc, char **argv)
{
  unsigned int i, nrecords = strtol (argv [1], 0, 10);
  FILE *f;

  f = fopen ("hugefile.db", "w");

  for (i = 0; i < nrecords; ++i)
    {
      struct record r;
      r.group = rand () % 100;

      fwrite (&r, sizeof r, 1, f);
    }

  fclose (f);
  return 0;
}

--- read-hugefile.c ---

#include <stdio.h>
#include <errno.h>
#include <stdlib.h>

#include "hugefile.h"

FILE *in;
FILE *out[100];

int
main ()
{
  int i;
  char name [128];
  in = fopen ("hugefile.db", "r");

#ifdef BUFFER
  setvbuf (in, malloc (2*BUFFER), _IOFBF, 2*BUFFER);
#endif

  for (i = 0; i < 100; ++i)
    {
      sprintf (name, "out/file%03d.db", i);
      out [i] = fopen (name, "w");
#ifdef BUFFER
      setvbuf (out [i], malloc (BUFFER), _IOFBF, BUFFER);
#endif
    }

  struct record r;
  while ((i = fread (&r, sizeof r, 1, in)) == 1)
    fwrite (&r, sizeof r, 1, out [r.group]);

  fflush (0);
  return 0;
}

velco@sue:~/tmp/hugefile$ ls
gen-hugefile.c  hugefile.h  read-hugefile.c
velco@sue:~/tmp/hugefile$ gcc -O2 gen-hugefile.c -o gen-hugefile
velco@sue:~/tmp/hugefile$ ./gen-hugefile 1000000
velco@sue:~/tmp/hugefile$ ls -lh
total 978M
-rwxrwxr-x 1 velco velco 8.5K Dec 14 13:33 gen-hugefile
-rw-rw-r-- 1 velco velco  364 Dec 14 13:31 gen-hugefile.c
-rw-rw-r-- 1 velco velco 977M Dec 14 13:34 hugefile.db
-rw-rw-r-- 1 velco velco   61 Dec 14 12:56 hugefile.h
-rw-rw-r-- 1 velco velco  603 Dec 14 13:32 read-hugefile.c
velco@sue:~/tmp/hugefile$ gcc -O2 read-hugefile.c -o read-hugefile
velco@sue:~/tmp/hugefile$ gcc -O2 -DBUFFER=1048576 read-hugefile.c -o read-hugefile-buf
velco@sue:~/tmp/hugefile$ mkdir out
velco@sue:~/tmp/hugefile$ time ./read-hugefile

real    0m34.031s
user    0m0.716s
sys 0m6.204s
velco@sue:~/tmp/hugefile$ time ./read-hugefile

real    0m25.960s
user    0m0.600s
sys 0m6.320s
velco@sue:~/tmp/hugefile$ time ./read-hugefile-buf

real    0m20.756s
user    0m1.528s
sys 0m5.420s
velco@sue:~/tmp/hugefile$ time ./read-hugefile-buf

real    0m16.450s
user    0m1.324s
sys 0m5.012s
velco@sue:~/tmp/hugefile$ 
于 2012-12-14T10:53:42.813 回答
1

在一个进程中只有 100 个打开的文件,或者在一个目录中只有 100 个文件,不应该成为现代系统的瓶颈。但是可以同时随机访问 101 个文件和总共 2 GB 的数据。

我会这样做:

从大文件中读取一些记录,将每种类型的记录存储到内存中自己的列表中。读取大约 10 兆字节的记录可能足够大,您将获得不错的性能,但这取决于可用的 RAM(您不想使用太多以至于操作系统开始为此使用交换文件......)。

然后一次一个地浏览内存中的 100 个记录列表,并一次追加到一个文件。您可以保持所有文件打开,这可能不是问题,但您也可以根据需要尝试关闭和打开它们,这样一次处理一个文件不会有太多开销。

于 2012-12-14T11:42:08.577 回答
0

听起来您可以在内存中对它们进行排序,然后一次将它们写出一组。

于 2012-12-14T10:27:18.350 回答