내 대답은 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
했지만 스레드의 오버 헤드는 이러한 간단한 작업 (문자열 분할, 쓰기)에 대한 이점보다 완전히 중요합니다.
펄
awk
( gawk
here) 솔직히 이와 같은 데이터 테스트를위한 첫 번째 요청 포트가 될 것이지만 Perl에서도 상당히 비슷한 일을 할 수 있습니다. 비슷한 구문이지만 약간 더 나은 쓰기 핸들이 있습니다.
perl -ane 'open(my $fh, ">", $F[0].".seq"); print $fh $F[1]; close $fh;' infile
파이썬
나는 파이썬을 좋아한다 . 그것은 나의 하루 직업 언어이고 단지 훌륭하고 견고하며 믿을 수 없을 정도로 읽기 쉬운 언어입니다. 초보자조차도 여기서 무슨 일이 일어나고 있는지 추측 할 수 있습니다.
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
바이너리가 파이썬의 유일한 구현은 아니라는 것을 기억해야합니다 . Pypy를 통해이 동일한 테스트를 실행했을 때 더 이상의 논리 최적화가 없으면 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 병렬
(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 시간 만 절약합니다.