簡體   English   中英

讀取文本列的大型數據文件的最快方法是什么?

[英]What is the fastest way to read in a large data file of text columns?

我有一個近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例程? 我很快意識到將數據存儲在元組數組中比為每個條目實例化一個類要好。

一些要點:

  1. 你的C例程是作弊; 它正在被文件大小提示,並且正在預先分配......

  2. Python:考慮使用array.array('d') ... S和nu各一個。 然后嘗試預分配。

  3. Python:將您的例程編寫為函數並調用它 - 訪問函數局部變量比訪問模塊全局變量要快得多。

在C實現中,您可以嘗試交換較低級別系統調用open() / read() / close()fopen() / fread() / fclose()庫函數。 加速可能來自fread()執行大量緩沖的事實,而read()則不會。

此外,使用更大的塊更少地調用read()將減少系統調用的數量,因此用戶空間和內核空間之間的切換更少。 發出read()系統調用時內核執行的操作(如果從fread()庫函數調用它並不重要)是從磁盤讀取數據然后將其復制到用戶空間。 如果您在代碼中經常發出系統調用,則復制部分會變得昂貴。 通過讀入更大的塊,您最終將減少上下文切換並減少復制。

請記住,盡管read()不能保證返回您想要的確切字節數的塊。 這就是為什么在可靠和正確的實現中,您始終必須檢查read()的返回值。

可能適用於C,C ++和python版本的方法是使用內存映射文件。 最顯着的好處是,當數據從一個緩沖區復制到另一個緩沖區時,它可以減少數據的雙重處理量。 在許多情況下,由於I / O的系統調用數量減少,因此也有好處。

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);

考慮到你需要執行它的次數,另一種可能的加速是使用指向S和nu的指針而不是索引到數組中,例如,

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

此外,由於您總是在buf中的相同位置從char轉換為double,因此預先計算循環外的地址,而不是每次在循環中計算它們。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM