Phản hồi nhanh của tôi sẽ có awk
nhưng nếu bạn đang xử lý nhiều dòng thì tôi đang nói về hàng triệu người, bạn sẽ thấy lợi ích thực sự từ việc chuyển sang ngôn ngữ lập trình "thực".
Với ý nghĩ đó (và awk
đã được coi là một câu trả lời) Tôi đã viết một vài triển khai bằng các ngôn ngữ khác nhau và đánh giá chúng trên cùng một bộ dữ liệu 10.000 dòng trên SSD PCI-E.
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
Nhìn thoáng qua, C trông có vẻ tốt nhất nhưng nó là một con lợn để chạy nhanh như vậy. Pypy và C ++ dễ dàng hơn nhiều để viết và thực hiện đủ tốt trừ khi bạn nói về nhiều tỷ dòng. Nếu đó là trường hợp, một bản nâng cấp để thực hiện tất cả trong RAM hoặc trên SSD có thể là một khoản đầu tư tốt hơn so với cải tiến mã.
Rõ ràng trong khoảng thời gian tôi trải qua, bạn có thể đã xử lý vài trăm triệu bản ghi trong tùy chọn chậm nhất . Nếu bạn chỉ có thể viết awk
hoặc vòng lặp Bash, hãy làm điều đó và tiếp tục với cuộc sống. Tôi rõ ràng đã có quá nhiều thời gian rảnh rỗi ngày hôm nay.
Tôi cũng đã thử nghiệm một số tùy chọn đa luồng (trong C ++ và Python và hybrid với GNU parallel
) nhưng tổng phí của các luồng hoàn toàn vượt trội hơn bất kỳ lợi ích nào cho một thao tác đơn giản như vậy (tách chuỗi, viết).
Perl
awk
( gawk
ở đây) thực sự sẽ là cổng gọi đầu tiên của tôi để kiểm tra dữ liệu như thế này nhưng bạn có thể thực hiện những điều tương tự trong Perl. Cú pháp tương tự nhưng với một tay cầm viết tốt hơn một chút.
perl -ane 'open(my $fh, ">", $F[0].".seq"); print $fh $F[1]; close $fh;' infile
Con trăn
Tôi thích Python. Đó là ngôn ngữ công việc hàng ngày của tôi và nó chỉ là một ngôn ngữ tốt đẹp, vững chắc và cực kỳ dễ đọc. Ngay cả một người mới bắt đầu cũng có thể đoán những gì đang xảy ra ở đây.
with open("infile", "r") as f:
for line in f:
id, chunk = line.split()
with open(id + ".seq", "w") as fw:
fw.write(chunk)
Bạn phải nhớ rằng python
nhị phân phân phối của bạn không phải là triển khai Python duy nhất ngoài đó. Khi tôi chạy thử nghiệm tương tự này thông qua Pypy, nó nhanh hơn C mà không cần tối ưu hóa logic nữa. Hãy ghi nhớ điều đó trước khi viết Python thành "ngôn ngữ chậm".
C
Tôi đã bắt đầu ví dụ này để xem những gì chúng ta thực sự có thể khiến CPU của mình làm được nhưng thật lòng mà nói, C là một cơn ác mộng khi viết mã nếu bạn không chạm vào nó trong một thời gian dài. Điều này có thêm nhược điểm là bị giới hạn ở các dòng 100 char mặc dù rất đơn giản để mở rộng điều đó, tôi chỉ không cần nó.
Phiên bản gốc của tôi chậm hơn C ++ và pypy nhưng sau khi viết blog về nó, tôi đã nhận được sự giúp đỡ từ Julian Klode . Phiên bản này bây giờ là nhanh nhất vì bộ đệm IO được điều chỉnh của nó. Nó cũng là một nhiều hơn và tham gia nhiều hơn so với bất cứ điều gì khác.
#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 ++
Thực hiện tốt và là nhiều dễ dàng hơn để viết hơn thực C. Bạn có tất cả các loại điều mà nắm tay bạn (đặc biệt là khi nói đến các chuỗi và đầu vào). Tất cả điều đó có nghĩa là bạn thực sự có thể đơn giản hóa logic xuống. strtok
trong C là một con heo vì nó xử lý toàn bộ chuỗi và sau đó chúng ta cần thực hiện tất cả việc phân bổ bộ nhớ mệt mỏi đó. Điều này chỉ chạy dọc theo dòng cho đến khi nó chạm vào tab và chúng tôi kéo các phân đoạn ra khi chúng tôi cần chúng.
#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();
}
Song song GNU
(Không phải phiên bản moreutils). Đó là một cú pháp ngắn gọn súc tích nhưng OMGSLOW. Tôi có thể đang sử dụng nó sai.
parallel --colsep '\t' echo {2} \> {1}.seq <infile
Kiểm tra máy phát điện khai thác
Đây là trình tạo dữ liệu của tôi cho 100000 dòng [ATGC] * 64. Nó không nhanh và cải tiến rất đáng hoan nghênh.
cat /dev/urandom | tr -dc 'ATGC' | fold -w 64 | awk 'NR>100000{exit}{printf NR"\t"$0"\n"}' > infile
awk
vẫn là một câu trả lời tốt cho bất cứ điều gì ít hơn hàng chục triệu. Ngay cả khi bạn [tuyến tính] quy mô này lên tới một tỷ dòng, C chỉ tiết kiệm cho bạn 1,5 giờ so với Perl và 3,6 giờ so với awk.