Jalankan perintah awk paralel pada file besar, tanpa ruang file tambahan

2

Saya mencoba untuk memproses file tab-separated yang cukup besar (~ 30GB). Saya dapat menjalankan awk untuk mengatur ulang kolom, tetapi hanya menggunakan satu inti dari mesin 8 inti saya. Bagaimana saya bisa menggunakan semua core? (dan secara dramatis mengurangi waktu pemrosesan saya) tanpa secara fisik memisahkan file dan menggunakan lebih banyak ruang disk?

cut -f3,4,5,6 original.tsv | awk 'BEGIN { FS="\t" }; { print $2"\t"$1"\t"$3"\t"$4 }' > abridged.tsv

Saya ingin melakukan sesuatu seperti ini, tetapi tanpa menggunakan lebih banyak ruang disk:

split -l 50000 original.tsv sp
for i in $(dir sp*); do ./process_file.sh $i & done;

di mana process_file.sh pada dasarnya adalah pernyataan cut / awk di atas.

Sekali lagi, masalah utama di sini adalah melakukan proses ini tanpa menggunakan 30GB lain! Ada saran?

MadmenDiver
sumber
1
Apakah Anda yakin CPU adalah hambatan, bukan IO?
Marco
1
Anda tidak membutuhkannya cut perintah cukup cetak $ 4, $ 3, $ 5, $ 6 (dan atur OFS ke tab)
RedGrittyBrick
1
1. I / O adalah bottleneck, bukan core. 2. Ini bukan tugas yang dapat diparalelkan dengan baik, kecuali jika setiap baris adalah jumlah bit yang ditentukan. Karena Anda juga ingin mengedit di tempat, saya sarankan untuk pergi ke Perl untuk operasi. Pengeditan di tempat adalah agak mungkin dengan awk dan beberapa tipu redirection , tapi solusi Perl lebih kuat dalam aspek ini, saya rasa. Selain itu, saat menguji solusinya, pastikan Anda memiliki cadangan file asli (bahkan jika itu 30GB) atau Anda mungkin menyesal.
Daniel Andersson
Saya tidak tahu banyak tentang itu awk (dan karena itu tidak bisa mengatakan banyak tentang itu) tapi saya pikir itu mungkin mencerahkan diskusi Anda jika saya melempar ini dalam diskusi Anda.
wullxz

Jawaban:

1

Karena Anda menggunakan "split" untuk memisahkan file Anda dengan jumlah baris, kemudian memprosesnya secara terpisah menghasilkan file yang berbeda (saya kira), Anda bisa melakukan beberapa perintah "awk" yang masing-masing hanya memproses bagian file Anda berdasarkan nomor baris:

$ cut -f3,4,5,6 original.tsv | awk 'BEGIN { FS="\t" }; (NR < 50000){ print $2"\t"$1"\t"$3"\t"$4 }' > abridged01.tsv
$ cut -f3,4,5,6 original.tsv | awk 'BEGIN { FS="\t" }; ((NR >= 50000) && (NR < 100000)){ print $2"\t"$1"\t"$3"\t"$4 }' > abridged02.tsv
$ cut -f3,4,5,6 original.tsv | awk 'BEGIN { FS="\t" }; ((NR >= 100000) && (NR < 150000)){ print $2"\t"$1"\t"$3"\t"$4 }' > abridged03.tsv

NR adalah variabel internal untuk "awk" yang berisi nomor baris saat ini. Setiap perintah hanya akan memproses garis dalam jangkauan mereka. TAPI , mereka semua akan melewati jalur lain juga, karena mereka perlu menghitungnya. Saya cukup yakin itu tidak akan membantu Anda, karena Anda kemungkinan besar akan jatuh dalam kemacetan IO. Tetapi Anda akan memiliki banyak proses, yang memungkinkan Anda untuk menggunakan banyak CPU, jika itu yang Anda inginkan. ;-)

Sekarang, JIKA Anda memiliki semua baris dengan panjang yang sama dalam byte, Anda pasti dapat melakukan paralelisasi nyata. Dalam hal ini, Anda akan menggunakan "dd" untuk mengekstrak porsi yang tepat untuk setiap proses "awk". Anda akan melakukan sesuatu yang mirip dengan:

dd if=original.tsv bs=30 count=50000 skip=0 | cut -f3,4,5,6 | awk 'BEGIN { FS="\t" }; { print $2"\t"$1"\t"$3"\t"$4 }' > abridged01.tsv

dd if=original.tsv bs=30 count=50000 skip=50000 | cut -f3,4,5,6 | awk 'BEGIN { FS="\t" }; { print $2"\t"$1"\t"$3"\t"$4 }' > abridged02.tsv

dd if=original.tsv bs=30 count=50000 skip=100000 | cut -f3,4,5,6 | awk 'BEGIN { FS="\t" }; { print $2"\t"$1"\t"$3"\t"$4 }' > abridged03.tsv

Di mana 30 adalah jumlah byte di setiap baris. Jika baris Anda tidak semua ukurannya sama dalam byte (yang merupakan kemungkinan terbesar) tapi Anda tahu byte yang tepat di mana blok garis Anda mulai dan berakhir, Anda masih bisa melakukannya menggunakan dd. Pelajari parameternya. Akhirnya, jika Anda tidak tahu di mana blok mulai dan berakhir, Anda dapat menemukannya dengan perintah awk tambahan. Tapi itu menambahkan pembacaan ekstra lengkap dalam file Anda. Kecuali jika Anda akan memproses file original.tsv Anda beberapa kali dengan cara yang berbeda, Anda tentu akan menghabiskan lebih banyak waktu pra-pemrosesan (menghitung byte di mana blok baris dimulai dan berakhir) dan kemudian memproses (yang mungkin akan memiliki keuntungan kecil karena Anda tentu akan memiliki bottleneck IO) daripada jika Anda hanya menggunakan solusi yang sudah Anda ketahui.

Bagaimanapun, sekarang Anda memiliki informasi dan opsi. ;-) Semoga berhasil! (y)

Mauricio
sumber
0

Fwiw, si split perintah memiliki opsi --filter=./myscript.sh yang memproses data sebelum ditulis ke file keluaran. Jadi, Anda dapat memiliki skrip pra-pemrosesan yang disebut ./myscript.sh mengandung transformasi Anda, mis.,

cut ... | awk '...' > $FILE

dimana $FILE adalah file output yang dihasilkan oleh split (misalnya xaa, ...). Atau, untuk menghindari semua file temp, cukup menyatukan ke satu file,

cut ... | awk '...' >> abridged.tsv

Tidak ada alasan untuk percaya ini akan lebih cepat daripada tidak menggunakan splitNamun demikian.

Mempertimbangkan bahwa Anda sedang melakukan pemrosesan "data besar" (dan berapa tahun setelah pertanyaan ini awalnya diajukan), mungkin juga mudah untuk menyalin file 30GB ini ke HDFS (atau, lebih tepatnya, menempatkan & menyimpan data tersebut) ada awalnya) dan gunakan sarang apache untuk memilih / memformat data yang diinginkan.

michael
sumber