我正在尝试读取一个大文件,该文件在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);
}
答案 0 :(得分:4)
几年前,我遇到了类似的问题。解决方案是将fscanf
替换为fgets
和strtod
。如果我没记错的话,这带来了十倍的改进。
所以你的循环:
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:关于错误检查,输入文件可能很容易包含nan
或inf
之类的文本,可能来自上游错误。但是strtod
和fscanf
很高兴解析它们。这可能会在您的代码中引起神秘的问题。
但是它很容易检查。添加代码:
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;
}