Neden bir dosyayı açmak değişken içeriği okumaktan daha hızlı?


36

Bir bashkomut dosyasında /proc/dosyalardan çeşitli değerlere ihtiyacım var . Şimdiye kadar, dosyaları doğrudan bu şekilde grepping onlarca satır var:

grep -oP '^MemFree: *\K[0-9]+' /proc/meminfo

Bunu daha verimli hale getirmek için dosya içeriğini bir değişkende sakladım ve şöyle ifade ettim:

a=$(</proc/meminfo)
echo "$a" | grep -oP '^MemFree: *\K[0-9]+'

Dosyayı birkaç kez açmak yerine, bu dosyayı yalnızca bir kez açmalı ve daha hızlı olacağını düşündüğüm değişken içeriği elemeliyim - ama aslında daha yavaş:

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

Aynısı dashve için de geçerlidir zsh. /proc/Dosyaların özel durumunun bir nedeni olduğundan şüphelendim , ancak içeriğini /proc/meminfonormal bir dosyaya kopyaladığımda ve sonuçların aynı olduğunu:

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

Boruyu kurtarmak için burada bir string kullanmak, onu biraz daha hızlı hale getirir, ancak yine de dosyalarda olduğu kadar hızlı değildir:

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

Neden bir dosyayı açmak değişkenden aynı içeriği okumaktan daha hızlı?


@ l0b0 Bu varsayım hatalı değil, soru nasıl çıktığımı gösteriyor ve cevaplar bunun neden böyle olduğunu açıklıyor. Düzenlemeniz artık başlık sorusunu yanıtlamamaya devam ediyor: Durumun böyle olup olmadığını söylemiyorlar.
tatlı

Tamam, netleştirildi. Çünkü başlıkların çoğu büyük çoğunlukta yanlıştı, sadece belli hafıza haritalanmış özel dosyalar için değil.
lbb0

@ l0b0 Hayır, burada soruyorum o: “Ben özel durumunu şüpheli /proc/gerekçe olarak dosyalar, ama içeriğini kopyalamak zaman /proc/meminfodüzenli bir dosya ve kullanımına sonuçları aynı olduğunu:” Öyle değil özel /proc/dosyalar, normal dosyaları okumak da daha hızlıdır!
tatlı

Yanıtlar:


47

Burada, bu konuda değil bir dosya açılırken karşı bir değişkenin içeriği okuyan ama daha fazladan sürecini bölmek veya olmasın hakkında.

grep -oP '^MemFree: *\K[0-9]+' /proc/meminfoyürütür bir süreç çatallar grepaçılır /proc/meminfo(bellekte, sanal bir dosya, disk G / Ç dahil) okur ve eşleşen RegExp.

Bu işlemin en pahalı kısmı, işlemi başlatmak ve grep yardımcı programını ve kitaplık bağımlılıklarını yüklemek, dinamik bağlantıyı yapmak, yerel veri tabanını açmak, diskte düzinelerce (ancak bellekte önbelleğe alınmış) dosya açmaktır.

Okuma /proc/meminfoile ilgili kısım kıyaslandığında önemsizdir, çekirdeğin oradaki bilgiyi üretmek için grepçok az zamana ihtiyacı vardır ve okumak için çok az zamana ihtiyacı vardır .

Buna devam ederseniz strace -c, okumak için kullanılan bir open()ve bir read()sistem çağrısının başlaması /proc/meminfogereken herşeye kıyasla yer fıstığı olduğunu görürsünüz grep( strace -cçatalları saymaz).

İçinde:

a=$(</proc/meminfo)

Bu $(<...)ksh operatörünü destekleyen çoğu kabukta , kabuk dosyayı açar ve içeriğini okur (ve izleyen yeni satır karakterlerini çıkarır). bashfarklı ve çok daha az verimlidir, çünkü bunu yapmak için bir işlemi zorlar ve verileri bir boru aracılığıyla ebeveyne iletir. Ama burada, bir kez yapıldı, bu yüzden önemli değil.

İçinde:

printf '%s\n' "$a" | grep '^MemFree'

Kabuğun , eşzamanlı olarak çalışan, ancak bir boru aracılığıyla birbirleriyle etkileşime giren iki işlemi yumurtlaması gerekir . Bu boru oluşturma, yırtma ve ondan yazma ve okumanın biraz maliyeti var. Çok daha yüksek maliyet, ekstra bir işlemin ortaya çıkmasıdır. Süreçlerin programlanmasının da bazı etkileri vardır.

Zsh <<<işlecini kullanmanın biraz daha hızlı hale geldiğini görebilirsiniz:

grep '^MemFree' <<< "$a"

Zsh ve bash'ta, içeriği $ageçici bir dosyaya yazılarak yapılır , bu da fazladan bir işlem oluşturmaktan daha ucuzdur, ancak muhtemelen verileri derhal elde etmenize kıyasla size herhangi bir kazanç sağlamaz /proc/meminfo. Bu /proc/meminfo, her yinelemede geçici dosyanın yazımı yapılırken, diske kopyalayan yaklaşımınızdan daha az etkilidir .

dashburadaki karakter dizilerini desteklemiyor, ancak heredocs'u fazladan bir sürecin oluşturulmasını gerektirmeyen bir boru ile gerçekleştiriliyor. İçinde:

 grep '^MemFree' << EOF
 $a
 EOF

Kabuk bir boru oluşturur, bir işlem yapar. Çocuk grepborunun okuma ucu olarak stdiniyle yürütür ve ebeveyn içeriği borunun diğer ucuna yazar.

Ancak, bu boru taşıma ve işlem senkronizasyonunun hala verileri doğrudan almaktan daha pahalı olması muhtemeldir /proc/meminfo.

İçeriği /proc/meminfokısa ve üretmek için çok zaman alıyor. Bazı CPU çevrimlerini kaydetmek istiyorsanız, pahalı parçaları çıkarmak istersiniz: forking işlemleri ve çalışan harici komutlar.

Sevmek:

IFS= read -rd '' meminfo < /proc/meminfo
memfree=${meminfo#*MemFree:}
memfree=${memfree%%$'\n'*}
memfree=${memfree#"${memfree%%[! ]*}"}

bashÖrüntü eşlemesi çok yetersiz olsa da kaçının . İle zsh -o extendedglobkısaltabilirsiniz:

memfree=${${"$(</proc/meminfo)"##*MemFree: #}%%$'\n'*}

^Birçok kabukta özel olduğunu unutmayın (Bourne, balık, rc, es ve en azından genişletilmiş seçenekle zsh), alıntı yapmayı tavsiye ederim. Ayrıca, echoisteğe bağlı verileri üretmek için kullanılamayacağına da dikkat edin (bu nedenle printfyukarıdakileri kullanıyorum).


4
Durumda olan printfkabuk iki süreci yumurtlamaya ihtiyacı var, ancak olmadığını söylüyorlar printfbir kabuk yerleşik?
David Conrad

6
@DavidConrad Öyle ama çoğu kabukları hangi parçalar için boru hattı analiz etmeye kalkma olabilir cari işlemde çalıştırın. Sadece kendini çatallar ve çocukların anlamasını sağlar. Bu durumda, ana işlem iki kez çatallaşır; Sol taraftaki çocuk daha sonra bir yerleşik görür ve onu yürütür; sağ taraftaki çocuk görür grepve idam eder.
chepner

1
@DavidConrad, boru bir IPC mekanizmasıdır, bu nedenle her iki tarafın da farklı işlemlerde çalışması gerekir. İken A | B, AT & T ksh gibi bazı kabukları vardır ya o koşmak ZSH Bbir yerleşik veya bileşik veya işlev komutu ise cari kabuk sürecinde, bunu herhangi bilmiyorum çalışır Acari işlemde. Bir şey olursa, bunu yapmak için, SIGPIPE'yi Açocuk işleminde çalışıyormuş gibi karmaşık bir şekilde ele almaları gerekirdi ve davranışın Berken çıkarken çok şaşırtıcı olmaması için kabuğun sonlandırılması gerekmeden . BAna süreçte çalıştırmak çok daha kolaydır .
Stéphane Chazelas

Bash destekler<<<
D. Ben Knoble

1
@ D.BenKnoble, bashdestekleme demek istemedim <<<, sadece operatör ksh'den gelmiş zshgibi $(<...)geldi.
Stéphane Chazelas

6

İlk durumda, sadece grep yardımcı programını kullanıyor ve dosyadan bir şey buluyorsunuz /proc/meminfo, /procsanal bir dosya sistemidir, bu nedenle /proc/meminfodosya bellektedir ve içeriğini almak için çok az zaman harcar.

Fakat ikinci durumda, bir boru oluşturuyorsunuz, sonra bu komutu kullanarak ilk komutun çıktısını ikinci komuta geçiriyorsunuz, bu pahalı.

Aradaki fark /proc(bellekte olduğundan) ve borudan kaynaklanıyor, aşağıdaki örneğe bakın:

time for i in {1..1000};do grep ^MemFree /proc/meminfo;done >/dev/null

real    0m0.914s
user    0m0.032s
sys     0m0.148s


cat /proc/meminfo > file
time for i in {1..1000};do grep ^MemFree file;done >/dev/null

real    0m0.938s
user    0m0.032s
sys     0m0.152s


time for i in {1..1000};do echo "$a"|grep ^MemFree; done >/dev/null

real    0m1.016s
user    0m0.040s
sys     0m0.232s

1

Her iki durumda da harici bir komut çağırıyorsunuz (grep). Harici arama bir alt kabuk gerektirir. Bu kabuğu çatallamak gecikmenin temel nedenidir. Her iki durum da benzerdir, dolayısıyla: benzer bir gecikme.

Harici dosyayı yalnızca bir kez okumak ve (bir değişkenden) birden çok kez kullanmak istiyorsanız, kabuktan çıkmayın:

meminfo=$(< /dev/meminfo)    
time for i in {1..1000};do 
    [[ $meminfo =~ MemFree:\ *([0-9]*)\ *.B ]] 
    printf '%s\n' "${BASH_REMATCH[1]}"
done

Bu, grep çağrısı için 1 saniye yerine sadece yaklaşık 0.1 saniye sürer.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.