“Eko” neden “dokunuştan” daha hızlı?


116

Zaman damgasını, dizimdeki tüm xml dosyalarının geçerli zamanına (tekrarlı olarak) güncellemeye çalışıyorum. Mac OSX 10.8.5 kullanıyorum.

Yaklaşık 300.000 dosyada, aşağıdaki echokomut 10 saniye sürer :

for file in `find . -name "*.xml"`; do echo >> $file; done

Ancak, aşağıdaki touchkomut 10 dakika sürer ! :

for file in `find . -name "*.xml"`; do touch $file; done

Eko neden buraya dokunmaktan daha hızlı?


20
Sadece bir yan not: Sen do bu iki komutlar, eşdeğer yok olmadığını biliyoruz? En azından Unix / Linux için, echo >> $fileistek yeni bir satır ekleyecek $fileve böylece değiştirecek. OS / X için aynı olacağını varsayalım. Bunu istemiyorsan, kullan echo -n >> $file.
Dubu

2
Ayrıca touch `find . -name "*.xml"` yukarıdakilerin ikisinden de daha hızlı olmaz mıydı ?
elmo

4
Ya da sadece düşünün>>$file
gerrit

8
Açık soruya bir cevap değil, ama neden touchbu kadar çok kez çağırdın ? çok daha az kere find . -name '*.xml' -print0 | xargs -0 touchçağırır touch(muhtemelen sadece bir kere). Linux üzerinde çalışır, OS X üzerinde çalışmalıdır
Mike Renfro

3
@elmo argüman listesi çok uzun (kolayca, 300.000
dosyayla

Yanıtlar:


161

Bash'te, touchharici bir ikili dosyadır, ancak yerleşikecho bir kabuktur :

$ type echo
echo is a shell builtin
$ type touch
touch is /usr/bin/touch

Yana touchbir dış ikili olduğunu ve çağırmak touchdosya başına bir kere, kabuk 300.000 örneğini oluşturmalısınız touchuzun zaman alır.

echoBununla birlikte, bir kabuk yerleşiktir ve kabuk yerleşiklerin yürütülmesi hiç çatal gerektirmez. Bunun yerine, mevcut kabuk tüm işlemleri yapar ve hiçbir dış işlem yaratılmaz; bu kadar hızlı olmasının nedeni budur.

İşte kabuğun işlemlerinin iki profili. Kullanırken yeni işlemleri klonlamak için çok zaman harcandığını görebilirsiniz touch. /bin/echoKabuk yerleşik yerine kullanmak çok daha karşılaştırılabilir bir sonuç göstermelidir.


Dokunuş kullanarak

$ strace -c -- bash -c 'for file in a{1..10000}; do touch "$file"; done'
% time     seconds  usecs/call     calls    errors syscall
------ ----------- ----------- --------- --------- ----------------
 56.20    0.030925           2     20000     10000 wait4
 38.12    0.020972           2     10000           clone
  4.67    0.002569           0     80006           rt_sigprocmask
  0.71    0.000388           0     20008           rt_sigaction
  0.27    0.000150           0     10000           rt_sigreturn
[...]

Echo kullanarak

$ strace -c -- bash -c 'for file in b{1..10000}; do echo >> "$file"; done'
% time     seconds  usecs/call     calls    errors syscall
------ ----------- ----------- --------- --------- ----------------
 34.32    0.000685           0     50000           fcntl
 22.14    0.000442           0     10000           write
 19.59    0.000391           0     10011           open
 14.58    0.000291           0     20000           dup2
  8.37    0.000167           0     20013           close
[...]

1
Strace'i OS X'te derlediniz mi veya testinizi başka bir işletim sisteminde mi çalıştırdınız?
bmike

1
@bmike Testim Linux'ta, ancak ilke aynı.
Chris Down,

Tamamen aynı fikirdeyim - / bin / echo'nun / bin / touch'ın ne kadar yavaş olduğu ile ilgili ana soru hakkındaki yorumuma bakın, bu yüzden akıl yürütme sağlamdır. Sadece strace zamanlamasını çoğaltmak istedim ve dtruss / dtrace kullanarak başarısız oldum ve bash -c sözdizimi de OS X'te beklendiği gibi çalışmadı.
bmike

71

Diğerlerinin cevapladığı gibi , kullanım kabuğun içinde (genellikle gerekli olmasa da) yerleşik bir komuttan echodaha hızlı olacaktır . Kullanımı, beraberinde getirdiğiniz her dosya için yeni bir işlem başlatmanın çalıştırılmasıyla ilişkili çekirdek yükü ile birlikte dağıtılır .touchechotouch

Ancak, bu efekti elde etmenin en hızlı yolunun hala kullanmak olduğuna dikkat edin touch, ancak programı her dosya için bir kez çalıştırmak yerine , yalnızca birkaç kez çalıştırılmasını sağlamak için -execseçeneği kullanmak mümkündür find. Bu yaklaşım genellikle daha hızlı olacaktır, çünkü bir kabuk halkası ile ilişkili ek yükü önler:

find . -name "*.xml" -exec touch {} +

Kullanılması +(aksine \;) ile find ... -execbağımsız değişken olarak her dosya ile sadece bir kere mümkünse çalışır komutu. Argüman listesi çok uzunsa (300.000 dosyada olduğu gibi), limite yakın bir uzunluğa sahip bir argüman listesiyle ( ARG_MAXçoğu sistemde) çoklu çalışma yapılır .

Bu yaklaşımın bir başka avantajı, orijinal döngüde olmayan tüm boşluk karakterlerini içeren dosya isimleriyle sağlam davranmasıdır.


17
+1Bul +argümanı işaret ettiğim için. Sanırım birçok insan bunun farkında değil (ben değildim).
gerrit,

7
Tüm sürümleri findbu +argümana sahip değil . Benzer bir etkiyi borulara bağlayarak alabilirsiniz xargs.
Barmar

5
@Barmar, +parça POSIX tarafından gerekli, bu yüzden taşınabilir olmalıdır. -print0değil.
Graeme

1
Hala ara sıra sahip olmayan uygulamalarla karşılaşıyorum. YMMV.
Barmar

1
@ChrisDown, keşfettiğim bir şey, Busybox'ın findmevcut seçeneğe sahip olması, ancak sadece ;yüzeyin altındaki gibi davranması .
Graeme

29

echoyerleşik bir kabuktur. Öte yandan, touchharici bir ikilidir.

$ type echo
echo is a shell builtin
$ type touch
touch is hashed (/usr/bin/touch)

Kabuk builtins orada hiçbir programı yükleme dahil hiçbir havai yani orada olduğu gibi çok daha hızlı fork/ execiçeriyordu. Bu nedenle, bir yerleşik komutu harici bir komutla çok sayıda yürütürken önemli bir zaman farkı gözlemlersiniz.

Bu gibi yardımcı programların timekabuk yerleşikleri olarak kullanılabilir olmasının nedeni budur .

Kabuk yerleşiklerinin tam listesini şöyle söyleyebilirsiniz:

enable -p

Yukarıda bahsedildiği gibi, yardımcı programın yerleşik yapıya karşılık kullanılması, önemli bir performans düşüşüyle sonuçlanır. Yerleşik echo ve yardımcı programını kullanarak ~ 9000 dosya oluşturmak için harcanan zamanın istatistikleri aşağıdadırecho :

# Using builtin
$ time bash -c 'for i in {1000..9999}; do echo > $i; done'

real    0m0.283s
user    0m0.100s
sys 0m0.184s

# Using utility /bin/echo
$ time bash -c 'for i in {1000..9999}; do /bin/echo > $i; done'

real    0m8.683s
user    0m0.360s
sys 0m1.428s

Ve bence echoçoğu sistemde bir ikili var (benim için /bin/echo), bu yüzden yerleşik yerine bunu kullanarak zamanlama testlerini tekrar deneyebilirsiniz
Michael Mrozek

@MichaelMrozek Yerleşik ve ikili için zamanlama testleri eklendi.
devnull
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.