在C中高效读取扁平化文件

时间:2018-08-02 18:27:48

标签: c performance file io

我正在尝试读取一个大文件,该文件在C中每行具有一个浮点数。为此,我将以下代码放在一起。在对小数据进行测试时,它工作正常。但是,以这种方式读取6亿个数字时,速度非常慢。关于如何加快速度的任何想法?我通过python生成原始文件,因此重新格式化数据(例如,在一行中用逗号分隔多个数字)也是一种选择。对此方法为何如此缓慢的任何见解将不胜感激。

void read_file(float *W)
{
   FILE *fp;

   int i = 0;

// In this file, one row should contain only one NUMBER!!
// So flatten the matrix.
   if (fp = fopen("C:\\Users\\rohit\\Documents\\GitHub\\base\\numerical\\c\\ReadFile1\\Debug\\data.txt", "r")) {
      while (fscanf(fp, "%f", &W[i]) != EOF) {
         ++i;
      }
      fclose(fp);
   }

   fclose(fp);

   scanf("%d",&i);    
}

2 个答案:

答案 0 :(得分:4)

几年前,我遇到了类似的问题。解决方案是将fscanf替换为fgetsstrtod。如果我没记错的话,这带来了十倍的改进。

所以你的循环:

  while (fscanf(fp, "%f", &W[i]) != EOF) {
     ++i;
  }

应类似于:

  while (fgets(buf, sizeof buf, fp)) {
     W[i++] = strtod(buf, 0);
  }

编辑:错误检查始终是一个好主意。因此,将其添加到其中,简单的两层直线将增加到大约十行:

  char buf[80];
  errno = 0;
  while (!errno && fgets(buf, sizeof buf, fp)) {
      W[i++] = strtod(buf, 0);
  }
  if (errno) { // Maybe ERANGE or EINVAL from strtod, or a read error like EINTR
      int save = errno;
      printf("errno=%d reading line %d\n", save, i); // or perror()
      exit(1);
  }

编辑2:关于错误检查,输入文件可能很容易包含naninf之类的文本,可能来自上游错误。但是strtodfscanf很高兴解析它们。这可能会在您的代码中引起神秘的问题。

但是它很容易检查。添加代码:

  int bad = 0;
  for (int j = 0; j < i; j++)
      bad += !isnormal(W[j]); // check for nan, inf, etc.
  if (bad) {
     // ... handle error
  }

将其放在一个单独的简单循环中使编译器更容易优化(理论上),尤其是在使用#pragma GCC optimize ("unroll-loops")之类的情况下。

答案 1 :(得分:4)

(评论:这是我的第二个答案。)我在评论中看到了OP:

  

您碰巧在C中有一个示例可以偶然读取二进制浮点数吗?

二进制版本将彻底摧毁任何ascii版本。而且更短。

此处,OP的功能签名已更改为在返回W中包括最大浮点数,并返回实际从文件中读取的数字。

size_t read_file(float *W, size_t maxlen)
{
    FILE *fp = fopen("C:\\Users\\rohit\\Documents\\GitHub\\base\\numerical\\c\\ReadFile1\\Debug\\data.txt", "r");
    return fp ? fread(W, sizeof(float), maxlen, fp) : 0;
}

或者对于更快甚至更快的东西,您可以使用mmap ...。但这在Windows上不可用。


已添加:但是,无缓冲的I / O可能会更快。以下函数使用单个malloc和单个未缓冲的read将文件复制到堆。 (注意:尚未在大型文件上进行测试;可能需要open64。)

#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>  
#include <sys/stat.h>  

void *readFileToHeap(const char *file, int *len) {
   void *retval = 0;
   ssize_t cnt;
   struct stat st;
   const int fd = open(file, O_RDONLY, 0);
   if (fd < 0)
      return printf("Cannot open %s\n", file), (void *)0;
   if (fstat(fd, &st))
      return perror("fstat()"), close(fd), (void *)0;
   if (!(retval = malloc(st.st_size)))
      return perror("malloc()"), close(fd), (void *)0;
   cnt = read(fd, retval, st.st_size);
   close(fd); // not the best: could clobber errno
   if (cnt < 0)
      return perror("read()"), free(retval), (void *)0;
   if (cnt != st.st_size)
      return printf("Partial read %d\n", cnt), free(retval), (void *)0;
   *len = cnt;
   return retval;
}