我有一个近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条答案
按热度按时间xwmevbvl1#
一些要点:
1.你的C例程是作弊的;它被告知文件大小,并预先分配.
rwqw0loc2#
一种可能适用于C、C++和python版本的方法是使用内存Map文件。最显著的好处是它可以减少数据从一个缓冲区复制到另一个缓冲区时的双重处理量。在许多情况下,由于减少了I/O的系统调用次数,也有好处。
xkrw2x1b3#
在C实现中,可以尝试将
fopen()
/fread()
/fclose()
库函数交换为较低级别的系统调用open()
/read()
/close()
。此外,本发明还使用更大的块调用
read()
的频率更低,这将减少系统调用的数量,因此用户空间和内核空间之间的切换将更少。(不管它是从fread()
库函数调用的)是从磁盘读取数据,然后将其复制到用户空间。如果在代码中经常发出系统调用,复制部分的开销会很大。通过阅读更大的块,您最终将减少上下文切换和复制。请记住,
read()
并不能保证返回一个你想要的字节数的块。这就是为什么在一个可靠和正确的实现中,你总是必须检查read()
的返回值。vkc1a9a24#
在
fread()
中,1
和BPL
参数的使用方式是错误的(你使用的方式,它可能会读取一个部分行,你没有测试)。在你尝试使用返回的数据之前,你还应该测试fread()
的返回值。您可以通过每次阅读多行来加快C版本的速度
字符串
在支持
posix_fadvise()
的系统上,您也应该在打开文件后提前执行此操作:型
xt0899hw5#
另一个可能的加速,考虑到你需要做的次数,是使用指向S和nu的指针,而不是索引到数组中,例如,
字符串
此外,由于你总是在buf中的相同位置从char转换为double,所以在循环外预先计算地址,而不是在循环中每次都计算它们。
xjreopfe6#
不要处理C,除非 * 绝对必要 *。为了尝试模拟OP的需要,我有一个方便的3列
ASCII-only
.txt
文件,由'='
而不是' '
分隔,跨越大约
7.6 GiB
的148 million rows
。将同一个文件进行3次管道传输,得到了一些
444 mn rows
的合成输入(离OP
提到的500 mn
目标不远),并使用awk
收集了它的一些基本统计信息- (将第一列作为浮点值进行求和,并将第二列和第三列的列宽分组计数)awk处理了一个**
444 mn rows
,22.8 GiB
合成的平面文本文件,没有预先制作的索引,只有78.26 secs
**。以同样的速度处理900万行只会是:1.586秒
C
可能比shell脚本具有这样的速度时更麻烦。字符串