Dalam bash
skrip saya membutuhkan berbagai nilai dari /proc/
file. Sampai sekarang saya memiliki lusinan baris yang melihat file secara langsung seperti itu:
grep -oP '^MemFree: *\K[0-9]+' /proc/meminfo
Dalam upaya untuk menjadikannya lebih efisien, saya menyimpan konten file dalam sebuah variabel dan memahami bahwa:
a=$(</proc/meminfo)
echo "$a" | grep -oP '^MemFree: *\K[0-9]+'
Alih-alih membuka file beberapa kali ini hanya harus membukanya sekali dan mengambil konten variabel, yang saya anggap akan lebih cepat - tetapi sebenarnya lebih lambat:
bash 4.4.19 $ time for i in {1..1000};do grep ^MemFree /proc/meminfo;done >/dev/null
real 0m0.803s
user 0m0.619s
sys 0m0.232s
bash 4.4.19 $ a=$(</proc/meminfo)
bash 4.4.19 $ time for i in {1..1000};do echo "$a"|grep ^MemFree; done >/dev/null
real 0m1.182s
user 0m1.425s
sys 0m0.506s
Hal yang sama berlaku untuk dash
dan zsh
. Saya mencurigai keadaan khusus /proc/
file sebagai alasan, tetapi ketika saya menyalin konten /proc/meminfo
ke file biasa dan menggunakannya hasilnya sama:
bash 4.4.19 $ cat </proc/meminfo >meminfo
bash 4.4.19 $ time for i in $(seq 1 1000);do grep ^MemFree meminfo; done >/dev/null
real 0m0.790s
user 0m0.608s
sys 0m0.227s
Menggunakan string di sini untuk menyimpan pipa membuatnya sedikit lebih cepat, tetapi masih tidak secepat file:
bash 4.4.19 $ time for i in $(seq 1 1000);do <<<"$a" grep ^MemFree; done >/dev/null
real 0m0.977s
user 0m0.758s
sys 0m0.268s
Mengapa membuka file lebih cepat daripada membaca konten yang sama dari suatu variabel?
/proc/
file sebagai alasan, tapi ketika saya menyalin isi dari/proc/meminfo
sebuah file biasa dan penggunaan yang hasilnya sama:” Hal ini tidak khusus untuk/proc/
file, membaca file biasa juga lebih cepat!Jawaban:
Di sini, ini bukan tentang membuka file versus membaca konten variabel tetapi lebih lanjut tentang forking proses tambahan atau tidak.
grep -oP '^MemFree: *\K[0-9]+' /proc/meminfo
forks proses yang mengeksekusigrep
yang membuka/proc/meminfo
(file virtual, dalam memori, tidak ada disk I / O yang terlibat) membacanya dan cocok dengan regexp.Bagian yang paling mahal di dalamnya adalah proses forking dan memuat utilitas grep dan dependensi pustaka, melakukan penautan dinamis, membuka basis data lokal, puluhan file yang ada di disk (tetapi kemungkinan di-cache dalam memori).
Bagian tentang membaca
/proc/meminfo
tidak signifikan dibandingkan, kernel membutuhkan sedikit waktu untuk menghasilkan informasi di sana dangrep
membutuhkan sedikit waktu untuk membacanya.Jika Anda menjalankannya
strace -c
, Anda akan melihat satuopen()
dan saturead()
sistem panggilan yang digunakan untuk membaca/proc/meminfo
adalah kacang dibandingkan dengan segala sesuatu yang laingrep
untuk memulai (strace -c
tidak termasuk forking).Di:
Dalam kebanyakan shell yang mendukung
$(<...)
operator ksh itu, shell hanya membuka file dan membaca kontennya (dan menghapus karakter baris baru yang tertinggal).bash
berbeda dan jauh lebih tidak efisien dalam hal garpu proses untuk melakukan itu membaca dan meneruskan data ke induk melalui pipa. Tapi di sini, dilakukan sekali jadi tidak masalah.Di:
Shell perlu menelurkan dua proses, yang berjalan bersamaan tetapi berinteraksi satu sama lain melalui pipa. Penciptaan pipa, penghancuran, dan menulis serta membaca darinya memiliki sedikit biaya. Biaya yang jauh lebih besar adalah pemijahan proses ekstra. Penjadwalan proses juga memiliki dampak.
Anda mungkin menemukan bahwa menggunakan
<<<
operator zsh membuatnya sedikit lebih cepat:Dalam zsh dan bash, itu dilakukan dengan menulis konten
$a
dalam file sementara, yang lebih murah daripada menelurkan proses tambahan, tetapi mungkin tidak akan memberi Anda keuntungan dibandingkan dengan mendapatkan data langsung/proc/meminfo
. Itu masih kurang efisien daripada pendekatan Anda yang menyalin/proc/meminfo
pada disk, karena penulisan file temp dilakukan pada setiap iterasi.dash
tidak mendukung string di sini, tetapi heredocs diimplementasikan dengan pipa yang tidak melibatkan pemijahan proses tambahan. Di:Shell membuat pipa, mengarungi proses. Anak dieksekusi
grep
dengan stdin sebagai ujung pembacaan pipa, dan orang tua menulis konten di ujung pipa yang lain.Tetapi penanganan pipa dan sinkronisasi proses masih cenderung lebih mahal daripada hanya mendapatkan data secara langsung
/proc/meminfo
.Konten
/proc/meminfo
pendek dan tidak membutuhkan banyak waktu untuk menghasilkan. Jika Anda ingin menyimpan beberapa siklus CPU, Anda ingin menghapus komponen mahal: proses forking dan menjalankan perintah eksternal.Seperti:
Hindari
bash
meskipun pencocokan polanya sangat tidak efisien. Denganzsh -o extendedglob
, Anda dapat mempersingkatnya menjadi:Catatan yang
^
spesial di banyak shell (Bourne, fish, rc, es dan zsh dengan opsi extendedglob setidaknya), saya sarankan mengutipnya. Perhatikan juga bahwaecho
tidak dapat digunakan untuk menampilkan data yang sewenang-wenang (oleh karena itu saya menggunakan diprintf
atas).sumber
printf
Anda mengatakan shell perlu menelurkan dua proses, tetapi bukankahprintf
shell builtin?grep
dan mengeksekusi.A | B
, ada beberapa shell seperti AT&T ksh atau zsh yang berjalanB
dalam proses shell saat ini jika itu adalah perintah builtin atau compound atau fungsi, saya tidak tahu ada yang berjalanA
dalam proses saat ini. Jika ada, untuk melakukan itu, mereka harus menangani SIGPIPE dengan cara yang kompleks seolah-olahA
sedang berjalan dalam proses anak dan tanpa menghentikan shell agar perilaku tidak terlalu mengejutkan ketikaB
keluar lebih awal. Jauh lebih mudah dijalankanB
dalam proses induk.<<<
bash
tidak mendukung<<<
, hanya saja operator datang darizsh
seperti$(<...)
berasal dari ksh.Dalam kasus pertama Anda, Anda hanya menggunakan utilitas grep dan menemukan sesuatu dari file
/proc/meminfo
,/proc
adalah sistem file virtual sehingga/proc/meminfo
file ada di memori, dan itu membutuhkan sedikit waktu untuk mengambil kontennya.Tetapi dalam kasus kedua, Anda membuat pipa, lalu meneruskan output perintah pertama ke perintah kedua menggunakan pipa ini, yang mahal.
Perbedaannya adalah karena
/proc
(karena ada di memori) dan pipa, lihat contoh di bawah ini:sumber
Anda memanggil perintah eksternal dalam kedua kasus (grep). Panggilan eksternal memerlukan subkulit. Mencungkil cangkang adalah penyebab mendasar dari penundaan ini. Kedua kasus serupa, dengan demikian: penundaan yang sama.
Jika Anda ingin membaca file eksternal hanya sekali dan menggunakannya (dari variabel) beberapa kali jangan keluar dari shell:
Yang hanya memakan waktu sekitar 0,1 detik, bukannya 1 detik penuh untuk panggilan grep.
sumber