การตอบสนองอย่างรวดเร็วของฉันน่าจะเกิดขึ้นawk
แต่ถ้าคุณกำลังประมวลผลหลายบรรทัด - และฉันกำลังพูดถึงหลายล้านคน - คุณอาจเห็นประโยชน์ที่แท้จริงจากการเปลี่ยนเป็นภาษาการเขียนโปรแกรม "ของจริง"
เมื่อทราบawk
แล้ว(และเป็นคำตอบแล้ว) ฉันจึงเขียนการติดตั้งใช้งานในภาษาต่าง ๆ และทำการเปรียบเทียบกับชุดข้อมูล 10,000 บรรทัดเดียวกันบน PCI-E SSD
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 และ hybrids ด้วย GNU parallel
) แต่ค่าใช้จ่ายของเธรดนั้นมีมากกว่าประโยชน์ทั้งหมดสำหรับการดำเนินการอย่างง่าย (การแยกสตริงการเขียน)
Perl
awk
( gawk
ที่นี่) โดยสุจริตจะเป็นพอร์ตแรกของฉันที่เรียกว่าการทดสอบข้อมูลเช่นนี้ แต่คุณสามารถทำสิ่งที่ค่อนข้างคล้ายกันใน 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
ไบนารีของการแจกแจงไม่ใช่การใช้ Python เพียงอย่างเดียว เมื่อฉันรันการทดสอบเดียวกันผ่าน Pypy มันเร็วกว่า Cโดยไม่มีการเพิ่มประสิทธิภาพตรรกะใด ๆ เพิ่มเติม โปรดทราบว่าก่อนที่จะเขียน Python เป็น "ภาษาช้า"
ค
ฉันเริ่มตัวอย่างนี้เพื่อดูว่าเราสามารถทำให้ซีพียูของฉันทำอะไรได้จริงๆ แต่ตรงไปตรงมา C คือฝันร้ายที่จะเขียนโค้ดถ้าคุณไม่ได้แตะมันมาเป็นเวลานาน นี่มีข้อเสียเพิ่มเติมของการถูก จำกัด อยู่ที่ 100-char บรรทัด แต่มันง่ายมากที่จะขยายมันฉันแค่ไม่ต้องการมัน
รุ่นเดิมของฉันช้ากว่า C ++ และ pypy แต่หลังจากที่บล็อกเกี่ยวกับเรื่องนี้ผมได้รับความช่วยเหลือจากจูเลียน 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 ++
ดำเนินการได้ดีและมากง่ายต่อการเขียนกว่าซีจริงคุณมีทุกประเภทของสิ่งที่จับมือของคุณ (โดยเฉพาะอย่างยิ่งเมื่อมันมาถึงสตริงและการป้อนข้อมูล) ทั้งหมดนั่นหมายความว่าคุณสามารถทำให้ตรรกะง่ายขึ้นได้ 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
(ไม่ใช่รุ่นututils) มันเป็นไวยากรณ์ที่กระชับ แต่ OMGSLOW ฉันอาจจะใช้มันผิด
parallel --colsep '\t' echo {2} \> {1}.seq <infile
ทดสอบเครื่องกำเนิดไฟฟ้าเทียม
นี่คือเครื่องมือสร้างข้อมูลของฉันสำหรับ [100,000 ATGC] * 64 มันไม่เร็วและยินดีต้อนรับการปรับปรุงอย่างมาก
cat /dev/urandom | tr -dc 'ATGC' | fold -w 64 | awk 'NR>100000{exit}{printf NR"\t"$0"\n"}' > infile
awk
ยังคงเป็นคำตอบที่ดีสำหรับสิ่งที่น้อยกว่าหลายสิบล้าน แม้ว่าคุณจะเพิ่มขนาดเป็นเส้นตรงเป็นพัน ๆ เส้น แต่ C สามารถช่วยคุณประหยัดเวลาได้มากกว่า Perl และ 1.5 ชั่วโมงถึง 3.6 ชั่วโมงเท่านั้น