Penggunaan sumber daya menggunakan pipa dan di sini string

16

Kita bisa mendapatkan hasil yang sama menggunakan dua berikut bash,

echo 'foo' | cat

dan

cat <<< 'foo'

Pertanyaan saya adalah apa perbedaan antara keduanya sejauh sumber daya yang digunakan terkait dan mana yang lebih baik?

Pikiran saya adalah bahwa ketika menggunakan pipa kita menggunakan proses echodan pipa ekstra sementara di sini string hanya deskriptor file yang digunakan cat.

utlam
sumber

Jawaban:

17

Pipa adalah file yang dibuka dalam sistem file-dalam-kernel dan tidak dapat diakses sebagai file pada-disk biasa. Secara otomatis buffered hanya untuk ukuran tertentu dan pada akhirnya akan memblokir ketika penuh. Tidak seperti file yang bersumber pada perangkat blok, pipa berperilaku sangat seperti perangkat karakter, dan umumnya tidak mendukung lseek()dan data yang dibaca dari mereka tidak dapat dibaca lagi seperti yang mungkin Anda lakukan dengan file biasa.

String di sini adalah file biasa yang dibuat dalam sistem file yang di-mount. Shell membuat file dan mempertahankan deskriptornya sambil segera menghapus satu-satunya tautan sistem file (dan menghapusnya) sebelum ia menulis / membaca byte ke / dari file tersebut. Kernel akan mempertahankan ruang yang diperlukan untuk file sampai semua proses melepaskan semua deskriptor untuknya. Jika anak yang membaca dari deskriptor semacam itu memiliki kemampuan untuk melakukannya, ia dapat lseek()diulang dengan dan dibaca lagi.

Dalam kedua kasus token <<<dan |mewakili file-deskriptor dan belum tentu file itu sendiri. Anda bisa mendapatkan ide yang lebih baik tentang apa yang terjadi dengan melakukan hal-hal seperti:

readlink /dev/fd/1 | cat

...atau...

ls -l <<<'' /dev/fd/*

Perbedaan yang paling signifikan antara kedua file adalah bahwa di sini-string / doc cukup banyak urusan yang terjadi sekaligus - shell menulis semua data ke dalamnya sebelum menawarkan deskriptor baca hingga kepada anak. Di sisi lain, shell membuka pipa pada deskriptor yang sesuai dan memotong anak-anak untuk mengelola mereka untuk pipa - dan karenanya ditulis / dibaca secara bersamaan di kedua ujungnya.

Namun, perbedaan ini umumnya hanya benar. Sejauh yang saya ketahui (yang sebenarnya tidak terlalu jauh) ini berlaku untuk hampir semua shell yang menangani <<<short-hand di <<sini untuk pengalihan dokumen di sini dengan pengecualian tunggal yash. yash, busybox, dash, Dan lainnya ashvarian cenderung kembali di sini-dokumen dengan pipa, meskipun, dan pada mereka kerang benar-benar ada sedikit perbedaan antara kedua setelah semua.

Oke - dua pengecualian. Sekarang saya berpikir tentang hal itu, ksh93tidak benar-benar melakukan pipa sama sekali untuk |, tetapi menangani seluruh bisnis w / soket - meskipun tidak melakukan file tmp yang dihapus <<<*seperti kebanyakan orang lain. Terlebih lagi, itu hanya menempatkan bagian-bagian terpisah dari sebuah pipa di lingkungan subkulit yang merupakan semacam eufemisme POSIX untuk setidaknya itu bertindak seperti subkulit , dan bahkan tidak melakukan garpu.

Faktanya adalah bahwa patokan @ PSkocik (yang sangat berguna) hasil di sini dapat sangat bervariasi karena berbagai alasan, dan sebagian besar bergantung pada implementasi. Untuk pengaturan di sini-dokumen, faktor terbesar adalah tipe ${TMPDIR}file-sistem target dan konfigurasi / ketersediaan cache saat ini, dan masih lebih banyak jumlah data yang akan ditulis. Untuk pipa itu akan menjadi ukuran proses shell itu sendiri, karena salinan dibuat untuk garpu yang diperlukan. Dengan cara bashini mengerikan pada pengaturan pipa (untuk memasukkan pergantian $(perintah )) - karena besar dan sangat lambat, tetapi dengan ksh93itu hampir tidak ada perbedaan sama sekali.

Berikut cuplikan shell kecil lain untuk menunjukkan bagaimana shell membagi subkulit untuk sebuah pipa:

pipe_who(){ echo "$$"; sh -c 'echo "$PPID"'; }
pipe_who
pipe_who | { pipe_who | cat /dev/fd/3 -; } 3<&0

32059  #bash's pid
32059  #sh's ppid
32059  #1st subshell's $$
32111  #1st subshell sh's ppid
32059  #2cd subshell's $$
32114  #2cd subshell sh's ppid

Perbedaan antara apa pipelined pipe_who()laporan panggilan dan laporan dari satu run di shell saat ini adalah karena (subkulit ini )perilaku tertentu mengklaim pid induk shell di $$ketika diperluas. Meskipun bashsubkulit jelas merupakan proses yang terpisah, $$parameter shell khusus bukan sumber informasi ini yang dapat diandalkan. Namun, shshell anak subkulit tidak menolak untuk melaporkannya secara akurat $PPID.

mikeserv
sumber
Sangat membantu. Sistem file in-kernel, apakah ada nama untuknya? apakah ini berarti ada di ruang kernel?
utlamn
2
@utlamn - sebenarnya, ya - cukup pipefs . Semuanya dalam kernel - tetapi (selain dari hal-hal seperti FUSE) begitu pula semua file i / o .
mikeserv
10

Tidak ada pengganti untuk pembandingan:

pskocik@ProBook:~ 
$ time (for((i=0;i<1000;i++)); do cat<<< foo >/dev/null; done  )

real    0m2.080s
user    0m0.738s
sys 0m1.439s
pskocik@ProBook:~ 
$ time (for((i=0;i<1000;i++)); do echo foo |cat >/dev/null; done  )

real    0m4.432s
user    0m2.095s
sys 0m3.927s
$ time (for((i=0;i<1000;i++)); do cat <(echo foo) >/dev/null; done  )
real    0m3.380s
user    0m1.121s
sys 0m3.423s

Dan untuk jumlah data yang lebih besar:

TENMEG=$(ruby -e 'puts "A"*(10*1024*1024)')
pskocik@ProBook:~ 
$ time (for((i=0;i<100;i++)); do echo "$TENMEG" |cat >/dev/null; done  )

real    0m42.327s
user    0m38.591s
sys 0m4.226s
pskocik@ProBook:~ 
$ time (for((i=0;i<100;i++)); do cat<<< "$TENMEG" >/dev/null; done  )

real    1m26.946s
user    1m23.116s
sys 0m3.681s
pskocik@ProBook:~ 

$ time (for((i=0;i<100;i++)); do cat <(echo "$TENMEG") >/dev/null; done  )

real    0m43.910s
user    0m40.178s
sys 0m4.119s

Akan muncul versi pipa memiliki biaya pengaturan yang lebih besar tetapi pada akhirnya lebih efisien.

PSkocik
sumber
@ mikeserv Itu benar. Saya menambahkan patokan dengan jumlah data yang lebih besar.
PSkocik
2
echo foo >/dev/shm/1;cat /dev/shm/1 >/dev/nulltampaknya cepat dalam kedua kasus ...
user23013
@ user23013 Itu masuk akal. Saya tidak melihat mengapa salah satu echo "$longstring"atau <<<"$longstring"akan di-tweak untuk efisiensi dan dengan string pendek, efisiensi tidak terlalu penting.
PSkocik
Sangat menarik bahwa dalam kasus saya (pada Ubuntu 14.04, Intel quad core i7) cat <(echo foo) >/dev/nulllebih cepat daripada echo foo | cat >/dev/null.
pabouk
1
@ Prim Ya, itu akan menjadi pendekatan yang lebih baik, tetapi yang lebih baik tidak perlu khawatir tentang ini sama sekali dan menggunakan alat yang tepat untuk pekerjaan itu. Tidak ada alasan untuk berpikir bahwa heredocs akan disesuaikan dengan kinerja.
PSkocik