C语言 读取包含文本列的大型数据文件的最快方法是什么?

4dbbbstv  于 2023-11-17  发布在  其他
关注(0)|答案(6)|浏览(132)

我有一个近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例程的速度吗?我很快意识到,将数据存储在元组数组中比为每个条目示例化一个类要好。

xwmevbvl

xwmevbvl1#

一些要点:
1.你的C例程是作弊的;它被告知文件大小,并预先分配.

  1. Python:考虑使用array.array('d ')...分别为S和nu。然后尝试预分配。
  2. Python:把你的例程写成函数并调用它--访问函数局部变量比访问模块全局变量要快。
rwqw0loc

rwqw0loc2#

一种可能适用于C、C++和python版本的方法是使用内存Map文件。最显著的好处是它可以减少数据从一个缓冲区复制到另一个缓冲区时的双重处理量。在许多情况下,由于减少了I/O的系统调用次数,也有好处。

xkrw2x1b

xkrw2x1b3#

在C实现中,可以尝试将fopen()/fread()/fclose()库函数交换为较低级别的系统调用open()/read()/close()
此外,本发明还使用更大的块调用read()的频率更低,这将减少系统调用的数量,因此用户空间和内核空间之间的切换将更少。(不管它是从fread()库函数调用的)是从磁盘读取数据,然后将其复制到用户空间。如果在代码中经常发出系统调用,复制部分的开销会很大。通过阅读更大的块,您最终将减少上下文切换和复制。
请记住,read()并不能保证返回一个你想要的字节数的块。这就是为什么在一个可靠和正确的实现中,你总是必须检查read()的返回值。

vkc1a9a2

vkc1a9a24#

fread()中,1BPL参数的使用方式是错误的(你使用的方式,它可能会读取一个部分行,你没有测试)。在你尝试使用返回的数据之前,你还应该测试fread()的返回值。
您可以通过每次阅读多行来加快C版本的速度

#define LINES_PER_READ 1000
char buf[LINES_PER_READ][BPL];

/* ... */

   while (i < NTRANS && (num = fread(buf, BPL, LINES_PER_READ, fp)) > 0) {
      int line;

      for (line = 0; i < NTRANS && line < num; line++)
      {
          buf[line][BPL-1]='\0';
          sp = &buf[line][BPL-10]; S[i] = atof(sp);
          buf[line][BPL-11]='\0';
          sp = &buf[line][BPL-23]; nu[i] = atof(sp);
          if (nu[i]>numax) {numax=nu[i];}
          ++i;
      }
    }

字符串
在支持posix_fadvise()的系统上,您也应该在打开文件后提前执行此操作:

posix_fadvise(fileno(fp), 0, 0, POSIX_FADV_SEQUENTIAL);

xt0899hw

xt0899hw5#

另一个可能的加速,考虑到你需要做的次数,是使用指向S和nu的指针,而不是索引到数组中,例如,

double *pS = S, *pnu = nu;
...
*pS++ = atof(sp);
*pnu = atof(sp);
...

字符串
此外,由于你总是在buf中的相同位置从char转换为double,所以在循环外预先计算地址,而不是在循环中每次都计算它们。

xjreopfe

xjreopfe6#

不要处理C,除非 * 绝对必要 *。为了尝试模拟OP的需要,我有一个方便的3列ASCII-only.txt文件,由'='而不是' '分隔,
跨越大约7.6 GiB148 million rows

  • 这只是一个大文件的素数我使用的代码验证检查
  • 这3列是素数的以2为底的对数,以5个小数位为固定宽度,数字本身,直接ASCII十进制数字,以及十六进制值。

将同一个文件进行3次管道传输,得到了一些444 mn rows的合成输入(离OP提到的500 mn目标不远),并使用awk收集了它的一些基本统计信息- (将第一列作为浮点值进行求和,并将第二列和第三列的列宽分组计数)
awk处理了一个**444 mn rows22.8 GiB合成的平面文本文件,没有预先制作的索引,只有78.26 secs**。以同样的速度处理900万行只会是:
1.586秒
C可能比shell脚本具有这样的速度时更麻烦。

( for ___ in $$ $$$$ $$$$$$; do; pv -q < "$__"; done | pvE 0.1 in0 | mawk2 ; )  

        77.05s user 7.66s system 108% cpu 1:18.26 total
gcat -b  0.00s user 0.00s system   0% cpu 1:18.26 total

字符串

  • (为了可读性,删除了数百行)*
log2(_)-sum :: ~ 29790561307.14320755004882812-bits

11515                 3            3            34545
11370                 3            6            68655
10369                 3            9            99762
10154                 3            12           130224

10098                 3            15           160518
...
2222                  3            4449         14761953
2000                  6            6123         18273609
1111                  165          69522        105717363

1000                  177          90270        127527948
999                   252          90522        127779696
888                   330          124305       159575346
777                   432          170421       197709843

666                   975          232761       242405211
555                   924          341022       308075370
444                   1878         553710       412565496
333                   4317         969051       571686960

222                   35214        2756478      1036817178
111                   53748        7457043      1796338806
99                    80262        8224686      1876339980
88                    95472        9469041      1991870625

77                    124485       10676904     2090514033
66                    171246       12489018     2217919404
55                    280368       14771130     2354031702
44                    827844       20983845     2649656613

33                    1502661      30431553     3001060344
22                    7765587      59774289     3729032556
11                    21562245     433469169    9124979175
9                     2761992      443583456    9223360053

8                     414552       443998008    9226676469
7                     295692       444293700    9228746313
6                     148572       444442272    9229637745
5                     23955        444466227    9229757520

4                     3183         444469410    9229770252
3                     429          444469839    9229771539
2                     54           444469893    9229771647
3333                  3            855          3876831
1111                  114          38097        56141871
1000                  153          54039        72918171
999                   174          54213        73091997
888                   159          76497        94118478

777                   258          109593       121425312
666                   537          157764       155947470
555                   504          231174       200476329
444                   1212         366900       267599124

333                   4389         685701       388875105
222                   12837        1553802      619755666
111                   61842        6291783      1374531276
99                    66285        7030575      1451567319

88                    67101        7769130      1520132172
77                    149163       8927163      1614135813
66                    150294       10389507     1718099022
55                    243546       12483837     1842271380

44                    339105       15356979     1981646316
33                    692868       23890017     2301350703
22                    1083543      37376904     2664284580
11                    105021729    327544728    6535425114

9                     19497831     437237055    7612850553
8                     5280768      442517823    7655096697
7                     1395681      443913504    7664866464
6                     373872       444287376    7667109696

5                     163302       444450678    7667926206
4                     17544        444468222    7667996382
3                     1530         444469752    7668000972
2                     141          444469893    7668001254
mawk2 '
BEGIN { CONVFMT = OFMT = "%.250g"
            _ ^= +( FS = "=" )
                   OFS = "=| "
} {                __ += $_
        ___[     length( $++_ )  ]++
       ____[-_ + length($(_--+_))]++
} END {

    printf("\f\f\r\t log2(_)-sum :: ~ %29.17f-bits\n\n", __)
    _____(____,
    _____(___))
}
function _____(___, _, __, ____, _______) {
    
    _ = ""
    for (_ in ___) {
        _ = length(___)
        break
    }
    if (_) {

        NF *= NF = (__ ^= NF = !_) + \
          (____  =  __)

        for (_ in ___)__ < +_ && 
                      __ = +_

        _______ = "(000|^(1+|2+|3+|4+|5+|6+|7+|8+|9+|.....+))$"

        for (_ = ++__; --_; ) if (__ = +___[_]) { 

            $+NF += ($____ = _) * ($(____+____) = __)
            $(NF  - ____) += __

            if (_ ~ _______)
                print
         }
    }
    print "\f\r" (_ = (_ = "---=| ")_)_ (_)_ "---\f\n"
    return
}'

相关问题