"स्पर्श" की तुलना में "इको" इतना तेज़ क्यों है?


116

मैं अपनी निर्देशिका (पुनरावर्ती) में सभी xml फ़ाइलों पर वर्तमान समय में टाइमस्टैम्प को अद्यतन करने का प्रयास कर रहा हूं। मैं मैक OSX 10.8.5 का उपयोग कर रहा हूं।

लगभग 300,000 फ़ाइलों पर, निम्न echoकमांड में 10 सेकंड लगते हैं :

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

हालाँकि, निम्न touchआदेश में 10 मिनट लगते हैं ! :

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

यहाँ स्पर्श की तुलना में गूंज इतनी तेज़ क्यों है?


20
बस एक ओर टिप्पणी: आप करते हैं पता है कि उन दो आदेशों नहीं बराबर, क्या तुम नहीं? कम से कम यूनिक्स / लिनक्स के लिए, इस तरह echo >> $fileसे एक नई लाइन संलग्न करेगा $fileऔर इसे संशोधित करेगा। मुझे लगता है कि यह ओएस / एक्स के लिए समान होगा। यदि आप ऐसा नहीं चाहते हैं, तो उपयोग करें echo -n >> $file
डबू

2
इसके अलावा touch `find . -name "*.xml"` ऊपर के दोनों से भी तेज नहीं होगा ?
एल्मो

4
या सिर्फ विचार करें>>$file
gerrit

8
स्पष्ट प्रश्न का उत्तर नहीं है, लेकिन touchसभी में इतनी बार क्यों आमंत्रित किया गया है? बहुत कम बार find . -name '*.xml' -print0 | xargs -0 touchआक्रमण करता है touch(संभवतः केवल एक बार)। लिनक्स पर काम करता है, ओएस एक्स पर काम करना चाहिए
माइक रेनप्रो

3
@elmo तर्क सूची बहुत लंबी (आसानी से, 300.000 फाइलों के साथ ...)
रमनो

जवाबों:


161

बैश में, touchएक बाहरी बाइनरी है, लेकिन echoएक शेल बिलिन है :

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

चूंकि touchएक बाहरी बाइनरी है, और आप touchप्रति फ़ाइल एक बार इनवॉइस करते हैं , इसलिए शेल को 300,000 उदाहरण बनाने होंगे touch, जिसमें लंबा समय लगता है।

echoहालांकि, एक शेल बिलिन है, और शेल बिल्डरों के निष्पादन के लिए फोर्किंग की आवश्यकता नहीं है। इसके बजाय, मौजूदा शेल सभी ऑपरेशन करता है और कोई बाहरी प्रक्रिया नहीं बनाई जाती है; यही कारण है कि यह इतना तेज है।

शेल के संचालन के दो प्रोफाइल यहां दिए गए हैं। आप देख सकते हैं कि उपयोग करते समय नई प्रक्रियाओं को क्लोन करने में बहुत समय व्यतीत होता है touch/bin/echoशेल बिलिन के बजाय का उपयोग करके एक बहुत अधिक तुलनीय परिणाम दिखाना चाहिए।


स्पर्श का उपयोग करना

$ 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
[...]

प्रतिध्वनि का उपयोग करना

$ 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
क्या आपने OS X पर स्ट्रेस संकलित किया या किसी अन्य OS पर अपना परीक्षण चलाया?
bmike

1
@bmike मेरा परीक्षण लिनक्स पर है, लेकिन सिद्धांत समान है।
क्रिस डाउन

मैं पूरी तरह से सहमत हूं - मुख्य प्रश्न पर मेरी टिप्पणी देखें कि कैसे / बिन / प्रतिध्वनि इतनी धीमी / बिन / स्पर्श है जितना कि तर्क ध्वनि है। मैं बस स्ट्रेस के समय को पुन: उत्पन्न करना चाहता था और dtruss / dtrace का उपयोग करने में विफल रहा और बैश-सी सिंटैक्स ओएस एक्स पर अपेक्षा के अनुरूप काम नहीं करता है।
bmike

71

जैसा कि दूसरों ने उत्तर दिया है, का उपयोग echoकरना तेजी से होगा touchजैसा echoकि एक कमांड है जो आमतौर पर (शेल में निर्मित होने के लिए आवश्यक नहीं है)। इसका उपयोग कर्नेल ओवरहेड के साथ जुड़ा हुआ है, जो आपके द्वारा प्राप्त होने वाली प्रत्येक फ़ाइल के लिए एक नई प्रक्रिया शुरू करने से संबंधित है touch

हालाँकि, ध्यान दें कि इस आशय को प्राप्त करने का सबसे तेज़ तरीका अभी भी उपयोग करना है touch, लेकिन प्रत्येक फ़ाइल के लिए एक बार प्रोग्राम चलाने के बजाय, यह सुनिश्चित करने के लिए -execविकल्प का उपयोग करना संभव है findकि केवल कुछ ही बार चलाया जाए। यह दृष्टिकोण आमतौर पर तेजी से होगा क्योंकि यह शेल लूप से जुड़े ओवरहेड से बचा जाता है:

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

+(यदि विरोध किया गया है \;) का उपयोग करके find ... -execकेवल एक बार एक तर्क के रूप में प्रत्येक फ़ाइल के साथ संभव हो तो कमांड चलाता है। यदि तर्क सूची बहुत लंबी है (जैसा कि 300,000 फ़ाइलों के मामले में है) एक तर्क सूची के साथ कई रन बनाए जाएंगे, जिसकी सीमा सीमा के करीब है ( ARG_MAXअधिकांश प्रणालियों पर)।

इस दृष्टिकोण का एक और लाभ यह है कि यह सभी व्हाट्सएप पात्रों वाले फाइलनामों के साथ दृढ़ता से व्यवहार करता है जो मूल लूप के साथ ऐसा नहीं है।


17
+1पता लगाने के +तर्क की ओर इशारा करते हुए । मुझे लगता है कि बहुत से लोग इस बारे में जागरूक नहीं हैं (मैं नहीं था)।
गेरिट

7
नहीं के सभी संस्करणों findहै +तर्क। आप पाइपिंग द्वारा एक समान प्रभाव प्राप्त कर सकते हैं xargs
बरमार

5
@ बरमार, +भाग को पोसिक्स की आवश्यकता होती है, इसलिए पोर्टेबल होना चाहिए। -print0नहीं है।
ग्रीम

1
मैं अभी भी कभी-कभार ऐसे कार्यान्वयनों में भाग लेता हूं जो इसके पास नहीं हैं। YMMV।
बरमार

1
@ क्रिसडाउन, कुछ मुझे पता चला है कि बिजीबॉक्स findमें विकल्प उपलब्ध है, लेकिन इसे ;सतह के नीचे की तरह व्यवहार करता है ।
ग्रीम

29

echoशेल निर्मित है। दूसरी ओर, touchएक बाहरी बाइनरी है।

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

शेल बिल्डिंग्स बहुत तेज़ हैं क्योंकि प्रोग्राम को लोड करने में कोई उपरि शामिल नहीं है, अर्थात इसमें कोई शामिल fork/ execशामिल नहीं है। जैसे, आप बड़ी संख्या में किसी बाह्य कमांड को निष्पादित करते समय एक महत्वपूर्ण समय अंतर का निरीक्षण करेंगे।

यही कारण है कि उपयोगिताओं की तरह timeशेल बिल्डरों के रूप में उपलब्ध हैं।

आप शेल बिल्डरों की पूरी सूची यह कह कर प्राप्त कर सकते हैं:

enable -p

जैसा कि ऊपर उल्लेख किया गया है, एक महत्वपूर्ण प्रदर्शन गिरावट में अंतर्निहित परिणामों के विपरीत उपयोगिता का उपयोग करना । बिलिन और उपयोगिता का उपयोग करके ~ 9000 फाइलें बनाने के लिए लिए गए समय के आंकड़े निम्नलिखित हैं : echo echo

# 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

और मुझे लगता है वहाँ एक है echoसबसे सिस्टम पर बाइनरी (मेरे लिए यह है /bin/echo), तो आप का उपयोग कर निर्मित की बजाय उस समय परीक्षण पुन: प्रयास कर सकते हैं
माइकल Mrozek

@MichaelMrozek ने बिलिन और बाइनरी के लिए समय परीक्षण जोड़ा।
devnull
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.