私のスナップ応答はそうだったでしょうがawk
、もしあなたがたくさんの行を処理しているなら-そして私が何百万について話しているなら-あなたは「本当の」プログラミング言語に切り替えることから本当の利益を見るでしょう。
そのことを念頭に置いて(そしてawk
すでに答えとして受け取られています)、私はいくつかの異なる言語での実装を書き、PCI-E SSD上の同じ10,000行のデータセットでそれらをベンチマークしました。
me* (C) 0m1.734s
me (C++) 0m1.991s
me (Python/Pypy) 0m2.390s
me (perl) 0m3.024s
Thor+Glenn (sed|sh) 0m3.353s
me (python) 0m3.359s
jasonwryan+Thor (awk) 0m3.779s
rush (while read) 0m6.011s
Thor (sed) 1m30.947s
me (parallel) 4m9.429s
一見したところ、Cは最高に見えますが、それを速く走らせるのは豚でした。PypyとC ++は、何十億行もの話をしているのでなければ、十分に簡単に記述して実行できます。その場合、すべてをRAMまたはSSDで実行するようにアップグレードする方が、コードを改善するよりも良い投資になる可能性があります。
明らかに、これらの処理に費やした時間では、おそらく最も遅いオプションで数億件のレコードを処理できたでしょう。awk
ループの記述またはBash のみが可能な場合は、それを実行して、作業を続けます。今日は明らかに余暇が多すぎた。
また、いくつかのマルチスレッドオプション(C ++とPython、およびGNUとのハイブリッドparallel
)をテストしましたが、スレッドのオーバーヘッドは、このような単純な操作(文字列分割、書き込み)の利点を完全に上回ります。
Perl
awk
(gawk
ここ)正直に言って、このようなデータをテストするための最初の呼び出しポートですが、Perlでもかなり似たようなことができます。構文は似ていますが、書き込みハンドルがわずかに優れています。
perl -ane 'open(my $fh, ">", $F[0].".seq"); print $fh $F[1]; close $fh;' infile
Python
私は Pythonが好きです。それは私の日常の仕事の言語であり、素晴らしく、しっかりした、信じられないほど読みやすい言語です。初心者でもおそらくここで何が起こっているのか推測できるでしょう。
with open("infile", "r") as f:
for line in f:
id, chunk = line.split()
with open(id + ".seq", "w") as fw:
fw.write(chunk)
ディストリビューションのpython
バイナリは、Pythonの唯一の実装ではないことを覚えておく必要があります。この同じテストをPypyで実行したとき、それ以上のロジック最適化なしでCよりも高速でした。Pythonを「遅い言語」として書く前に、そのことを覚えておいてください。
C
CPUに実際に何をさせることができるかを確認するためにこの例を始めましたが、率直に言って、長い間触れなかった場合、Cはコードの悪夢です。これには100文字の行に制限されるというマイナス面がありますが、展開するのは非常に簡単ですが、必要ありませんでした。
私の元のバージョンはC ++やpypyよりも遅かったのですが、それについてブログを書いた後、Julian Klodeから助けを得ました。このバージョンは、IOバッファが調整されているため、最速です。まただたくさん長く、何よりも関与します。
#include <stdio.h>
#include <string.h>
#include <fcntl.h>
#include <stdlib.h>
#define BUFLEN (8 * 1024)
int main(void) {
FILE *fp;
FILE *fpout;
char line[100];
char *id;
char *token;
char *buf = malloc(BUFLEN);
fp = fopen("infile", "r");
setvbuf ( fp , buf , _IOLBF, BUFLEN );
while (fgets(line, 100, fp) != NULL) {
id = strtok(line, "\t");
token = strtok(NULL, "\t");
char *fnout = malloc(strlen(id)+5);
fnout = strcat(fnout, id);
fnout = strcat(fnout, ".seq");
fpout = fopen(fnout, "w");
setvbuf ( fpout , NULL , _IONBF , 0 );
fprintf(fpout, "%s", token);
fclose(fpout);
}
fclose(fp);
return 0;
}
C ++
優れたパフォーマンスを発揮し、実際のCよりもはるかに簡単に書くことができます。特に、文字列や入力に関しては、あらゆる種類のものがあります。つまり、実際にはロジックを単純化できます。strtok
Cでは、文字列全体を処理し、面倒なメモリ割り当てをすべて行う必要があるため、豚です。これは、タブに到達するまで線に沿って点滅し、必要に応じてセグメントを引き出します。
#include <fstream>
#include <string>
using namespace std;
int main(void) {
ifstream in("infile");
ofstream out;
string line;
while(getline(in, line)) {
string::size_type tab = line.find('\t', 0);
string filename = line.substr(0, tab) + ".seq";
out.open(filename.c_str());
out << line.substr(tab + 1);
out.close();
}
in.close();
}
GNU Parallel
(moreutilsバージョンではありません)。簡潔でわかりやすい構文ですが、OMGSLOWです。間違って使用している可能性があります。
parallel --colsep '\t' echo {2} \> {1}.seq <infile
テストハーネスジェネレーター
[ATGC] * 64の100000行のデータジェネレーターを次に示します。それは高速ではなく、改善は大歓迎です。
cat /dev/urandom | tr -dc 'ATGC' | fold -w 64 | awk 'NR>100000{exit}{printf NR"\t"$0"\n"}' > infile
awk
数千万未満の場合でもまだ良い答えです。これを[直線的に] 10億行に拡大しても、CはPerlで1.5時間、awkで3.6時間しか節約できません。