读取大文本列数据文件的最快方式是什么?
我有一个数据文件,里面有将近900万行(不久后会超过5亿行),我在寻找最快的读取方法。文件里的五列数据是用空格分开的,所以我知道每行中要找的两个字段的位置。
我用Python写的程序需要45秒:
import sys,time
start = time.time()
filename = 'test.txt' # space-delimited, aligned columns
trans=[]
numax=0
for line in open(linefile,'r'):
nu=float(line[-23:-11]); S=float(line[-10:-1])
if nu>numax: numax=nu
trans.append((nu,S))
end=time.time()
print len(trans),'transitions read in %.1f secs' % (end-start)
print 'numax =',numax
而我用C写的程序则快得多,只需要4秒:
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#define BPL 47
#define FILENAME "test.txt"
#define NTRANS 8858226
int main(void) {
size_t num;
unsigned long i;
char buf[BPL];
char* sp;
double *nu, *S;
double numax;
FILE *fp;
time_t start,end;
nu = (double *)malloc(NTRANS * sizeof(double));
S = (double *)malloc(NTRANS * sizeof(double));
start = time(NULL);
if ((fp=fopen(FILENAME,"rb"))!=NULL) {
i=0;
numax=0.;
do {
if (i==NTRANS) {break;}
num = fread(buf, 1, BPL, fp);
buf[BPL-1]='\0';
sp = &buf[BPL-10]; S[i] = atof(sp);
buf[BPL-11]='\0';
sp = &buf[BPL-23]; nu[i] = atof(sp);
if (nu[i]>numax) {numax=nu[i];}
++i;
} while (num == BPL);
fclose(fp);
end = time(NULL);
fprintf(stdout, "%d lines read; numax = %12.6f\n", (int)i, numax);
fprintf(stdout, "that took %.1f secs\n", difftime(end,start));
} else {
fprintf(stderr, "Error opening file %s\n", FILENAME);
free(nu); free(S);
return EXIT_FAILURE;
}
free(nu); free(S);
return EXIT_SUCCESS;
}
用Fortran、C++和Java写的程序耗时中等,分别是27秒、20秒和8秒。
我的问题是:在上面的代码中(特别是C代码),我有没有犯什么严重的错误?有没有办法让Python的程序更快一些?我很快意识到,把数据存储在元组的数组中比为每个条目创建一个类要好。
6 个回答
在C语言的实现中,你可以尝试用更底层的系统调用 open()
/read()
/close()
来替换 fopen()
/fread()
/fclose()
这些库函数。这样做可能会加快速度,因为 fread()
会进行很多缓冲处理,而 read()
则没有。
另外,减少 read()
的调用次数,使用更大的数据块,会减少系统调用的次数,这样你在用户空间和内核空间之间的切换也会减少。当你发出 read()
系统调用时(无论是通过 fread()
还是直接调用),内核会从磁盘读取数据,然后把这些数据复制到用户空间。如果你在代码中频繁发出这个系统调用,复制的过程就会变得很耗时。通过读取更大的数据块,你可以减少上下文切换和复制的次数。
不过要记住,read()
并不保证会返回你想要的确切字节数。这就是为什么在一个可靠和正确的实现中,你总是需要检查 read()
的返回值。
一种可能适用于C、C++和Python版本的方法是使用内存映射文件。这样做的最大好处是可以减少数据的重复处理,因为数据不需要从一个缓冲区复制到另一个缓冲区。在很多情况下,这样做还可以减少进行输入输出操作时的系统调用次数,从而提高效率。
几点说明:
你的C语言程序有点不太公平,它提前知道了文件大小,所以在分配内存的时候已经做好了准备...
在Python中,可以考虑使用 array.array('d') ... 分别为S和nu创建一个数组。然后再试试提前分配内存。
在Python中,把你的程序写成一个函数,然后调用它——这样访问函数内部的变量会比访问全局变量快很多。