मैंने निम्नलिखित परीक्षण किया है और मेरे सिस्टम पर परिणामी अंतर दूसरी स्क्रिप्ट के लिए लगभग 100 गुना लंबा है।
मेरी फाइल स्ट्रेस आउटपुट कहलाती है bigfile
$ wc -l bigfile.log
1617000 bigfile.log
स्क्रिप्ट
xtian@clafujiu:~/tmp$ cat p1.sh
tail -n 1000000 bigfile.log | grep '"success": true' | wc -l
tail -n 1000000 bigfile.log | grep '"success": false' | wc -l
xtian@clafujiu:~/tmp$ cat p2.sh
log=$(tail -n 1000000 bigfile.log)
echo "$log" | grep '"success": true' | wc -l
echo "$log" | grep '"success": true' | wc -l
मेरे पास वास्तव में grep के लिए कोई मैच नहीं है इसलिए अंतिम पाइप के माध्यम से कुछ भी नहीं लिखा जाता है wc -l
यहाँ समय हैं:
xtian@clafujiu:~/tmp$ time bash p1.sh
0
0
real 0m0.381s
user 0m0.248s
sys 0m0.280s
xtian@clafujiu:~/tmp$ time bash p2.sh
0
0
real 0m46.060s
user 0m43.903s
sys 0m2.176s
इसलिए मैंने स्ट्रेस कमांड के माध्यम से फिर से दो स्क्रिप्टों को चलाया
strace -cfo p1.strace bash p1.sh
strace -cfo p2.strace bash p2.sh
यहाँ निशान से परिणाम हैं:
$ cat p1.strace
% time seconds usecs/call calls errors syscall
------ ----------- ----------- --------- --------- ----------------
97.24 0.508109 63514 8 2 waitpid
1.61 0.008388 0 84569 read
1.08 0.005659 0 42448 write
0.06 0.000328 0 21233 _llseek
0.00 0.000024 0 204 146 stat64
0.00 0.000017 0 137 fstat64
0.00 0.000000 0 283 149 open
0.00 0.000000 0 180 8 close
...
0.00 0.000000 0 162 mmap2
0.00 0.000000 0 29 getuid32
0.00 0.000000 0 29 getgid32
0.00 0.000000 0 29 geteuid32
0.00 0.000000 0 29 getegid32
0.00 0.000000 0 3 1 fcntl64
0.00 0.000000 0 7 set_thread_area
------ ----------- ----------- --------- --------- ----------------
100.00 0.522525 149618 332 total
और P2.strace
$ cat p2.strace
% time seconds usecs/call calls errors syscall
------ ----------- ----------- --------- --------- ----------------
75.27 1.336886 133689 10 3 waitpid
13.36 0.237266 11 21231 write
4.65 0.082527 1115 74 brk
2.48 0.044000 7333 6 execve
2.31 0.040998 5857 7 clone
1.91 0.033965 0 705681 read
0.02 0.000376 0 10619 _llseek
0.00 0.000000 0 248 132 open
...
0.00 0.000000 0 141 mmap2
0.00 0.000000 0 176 126 stat64
0.00 0.000000 0 118 fstat64
0.00 0.000000 0 25 getuid32
0.00 0.000000 0 25 getgid32
0.00 0.000000 0 25 geteuid32
0.00 0.000000 0 25 getegid32
0.00 0.000000 0 3 1 fcntl64
0.00 0.000000 0 6 set_thread_area
------ ----------- ----------- --------- --------- ----------------
100.00 1.776018 738827 293 total
विश्लेषण
आश्चर्य की बात नहीं, दोनों मामलों में ज्यादातर समय एक प्रक्रिया को पूरा करने के लिए इंतजार में बिताया जाता है, लेकिन पी 2 की तुलना में 2.63 गुना अधिक समय तक इंतजार करता है, और जैसा कि दूसरों ने उल्लेख किया है, आप पी 2 में देरी से शुरू कर रहे हैं।
तो अब के बारे में भूल जाते हैं waitpid
, %
स्तंभ पर ध्यान न दें और दोनों निशान पर सेकंड कॉलम को देखो।
सबसे बड़ा समय p1 पढ़ने में अपना अधिकांश समय संभवतः समझ में आता है, क्योंकि पढ़ने के लिए एक बड़ी फ़ाइल है, लेकिन p1 की तुलना में पढ़ने में P2 28.82 गुना अधिक समय खर्च करता है। - bash
इतनी बड़ी फ़ाइल को एक चर में पढ़ने की उम्मीद नहीं है और शायद एक समय में बफर पढ़ रहा है, लाइनों में विभाजित हो रहा है और फिर एक और प्राप्त कर रहा है।
पी 1 के लिए रीड काउंट पी 2 705k बनाम 84k है, प्रत्येक को कर्नेल स्पेस में एक संदर्भ स्विच की आवश्यकता होती है और फिर से बाहर पढ़ा जाता है। लगभग 10 बार रीड और संदर्भ स्विच की संख्या।
लिखने में समय p2 41.93 गुना अधिक खर्च करता है p1 से लिखने में
लिखने की गिनती p1, P2, 42k बनाम 21k से अधिक लिखता है, हालांकि वे बहुत तेज हैं।
संभवतः पूंछ लिखने वाले बफ़र्स के विपरीत echo
लाइनों के कारण grep
।
इसके अलावा , P2 पढ़ने में लिखने की तुलना में अधिक समय खर्च करता है, p1 दूसरा तरीका है!
अन्य कारकbrk
सिस्टम कॉल की संख्या को देखें : P2 खर्च करता है 2.42 गुना अधिक ब्रेकिंग की तुलना में यह पढ़ता है! P1 में (यह रजिस्टर भी नहीं है)। brk
जब प्रोग्राम को अपने पते की जगह का विस्तार करने की आवश्यकता होती है, क्योंकि शुरू में पर्याप्त आवंटित नहीं किया गया था, तो यह संभवतः उस फ़ाइल को चर में पढ़ने के लिए बैश होने के कारण है, और यह उम्मीद नहीं है कि यह बड़ा हो, और जैसा @ @aiai ने उल्लेख किया है, अगर फ़ाइल बहुत बड़ी है, यहां तक कि काम नहीं करेगा।
tail
शायद काफी कुशल फ़ाइल रीडर है, क्योंकि यह वही है जो इसे करने के लिए डिज़ाइन किया गया था, यह संभवतः फ़ाइल को स्कैन करता है और लाइन ब्रेक के लिए स्कैन करता है, इस प्रकार कर्नेल को i / o को अनुकूलित करने की अनुमति देता है। बैश पढ़ना और लिखना दोनों समय बिताना उतना अच्छा नहीं है।
p2 44ms और में 41ms खर्च करता है clone
और execv
यह p1 के लिए एक औसत दर्जे का राशि नहीं है। संभवतः पूंछ से चर को पढ़ना और बनाना।
अंत में टोटल्स p1 ~ 150k सिस्टम कॉल बनाम P2 740k (4.93 गुना अधिक) निष्पादित करता है।
वेटपिड को खत्म करते हुए, पी 1 0.014416 सेकंड सिस्टम कॉलिंग, पी 2 0.439132 सेकंड (30 गुना लंबा) खर्च करता है।
ऐसा प्रतीत होता है कि P2 उपयोगकर्ता के स्पेस में अधिकतर समय बिताता है, सिवाय सिस्टम कॉल्स के पूरा होने के इंतजार के और कुछ भी नहीं करने के लिए और मेमोरी को फिर से व्यवस्थित करने के लिए, p1 अधिक लिखता है, लेकिन अधिक कुशल है और सिस्टम के कम लोड का कारण बनता है, और इसलिए तेजी से होता है।
निष्कर्ष
मैं बैश स्क्रिप्ट लिखते समय मेमोरी के माध्यम से कोडिंग के बारे में चिंता करने की कोशिश नहीं करूंगा, इसका मतलब यह नहीं है कि आप कुशल होने की कोशिश नहीं करते हैं।
tail
यह ऐसा करने के लिए डिज़ाइन किया गया है, यह संभवतः memory maps
फ़ाइल है ताकि यह पढ़ने में कुशल हो और कर्नेल को i / o को ऑप्टिमाइज़ करने की अनुमति दे।
अपनी समस्या को अनुकूलित करने का एक बेहतर तरीका हो सकता है कि आप grep
"" सफलता ": लाइनों और फिर ट्रस और फाल्स की गिनती करें, grep
एक गिनती विकल्प है जो फिर से बचा जाता है wc -l
, या इससे भी बेहतर अभी भी, पूंछ के माध्यम से पूंछ awk
और गिनती की गणना करें" समवर्ती रूप से झूठ बोलता है। P2 न केवल लंबे समय लेता है, बल्कि सिस्टम में लोड जोड़ता है, जबकि मेमोरी को ब्रक्स के साथ फेरबदल किया जा रहा है।