ทำไม“ echo” เร็วกว่า“ touch” มาก?


116

ฉันพยายามอัปเดตการประทับเวลาเป็นเวลาปัจจุบันของไฟล์ xml ทั้งหมดในไดเรกทอรีของฉัน (เรียกซ้ำ) ฉันใช้ Mac 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
เพียงแค่คำพูดจากด้านข้าง: คุณรู้หรือไม่ว่าคำสั่งทั้งสองนั้นไม่เทียบเท่ากัน อย่างน้อยสำหรับ Unix / Linux, echo >> $fileจะเพิ่มบรรทัดใหม่ให้$fileและทำการแก้ไข ฉันคิดว่ามันจะเหมือนกันสำหรับ OS / X echo -n >> $fileหากคุณไม่ต้องการที่จะใช้
Dubu

2
ยังจะไม่touch `find . -name "*.xml"` เร็วกว่าทั้งสองข้างต้นหรือไม่
elmo

4
หรือลองคิดดู>>$file
gerrit

8
ไม่ใช่คำตอบสำหรับคำถามที่ชัดเจน แต่ทำไมถึงเรียกใช้touchหลายครั้ง find . -name '*.xml' -print0 | xargs -0 touchเรียกใช้touchเวลาน้อยลง (อาจเป็นเพียงครั้งเดียว) ทำงานได้บน Linux ควรทำงานบน OS X
Mike Renfro

3
รายการอาร์กิวเมนต์ @elmo ยาวเกินไป (ง่ายด้วยไฟล์ 300.000 ... )
Rmano

คำตอบ:


161

ใน bash touchเป็นไบนารีภายนอก แต่echoเป็นshell builtin :

$ 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
คุณรวบรวม strace บน OS X หรือรันการทดสอบบน OS อื่นหรือไม่
bmike

1
@bmike การทดสอบของฉันใช้กับ Linux แต่หลักการเหมือนกัน
Chris Down

ฉันเห็นด้วยทั้งหมด - เห็นความคิดเห็นของฉันในคำถามหลักเกี่ยวกับวิธี / bin / echo ช้าเท่ากับ / bin / touch ดังนั้นการให้เหตุผลคือเสียง ฉันแค่ต้องการที่จะทำซ้ำจังหวะเวลาของ strace และล้มเหลวโดยใช้ dtruss / dtrace และไวยากรณ์ bash -c ไม่ทำงานตามที่คาดไว้ใน OS X เช่นกัน
bmike

71

ดังที่คนอื่น ๆ ตอบแล้วการใช้echoจะเร็วกว่าtouchที่echoเป็นคำสั่งซึ่งโดยทั่วไป (แม้ว่าไม่จำเป็นต้องมี) ในตัวเชลล์ ใช้มัน dispenses touchด้วยค่าใช้จ่ายเคอร์เนลที่เกี่ยวข้องกับการทำงานเริ่มต้นกระบวนการใหม่สำหรับแต่ละไฟล์ที่คุณได้รับ

อย่างไรก็ตามโปรดทราบว่าวิธีที่เร็วที่สุดในการบรรลุเอฟเฟกต์นี้ยังคงใช้อยู่touchแต่แทนที่จะเรียกใช้โปรแกรมหนึ่งครั้งสำหรับแต่ละไฟล์คุณสามารถใช้-execตัวเลือกด้วยfindเพื่อให้แน่ใจว่าใช้งานได้เพียงไม่กี่ครั้งเท่านั้น โดยทั่วไปวิธีการนี้จะเร็วกว่าเนื่องจากจะหลีกเลี่ยงโอเวอร์เฮดที่เกี่ยวข้องกับเชลล์ลูป:

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

การใช้+(ตรงข้ามกับ\;) กับfind ... -execรันคำสั่งเพียงครั้งเดียวหากเป็นไปได้กับแต่ละไฟล์เป็นอาร์กิวเมนต์ หากรายการอาร์กิวเมนต์ยาวมาก (เช่นกรณีที่มีไฟล์ 300,000 ไฟล์) การเรียกใช้หลายครั้งจะถูกสร้างขึ้นด้วยรายการอาร์กิวเมนต์ที่มีความยาวใกล้เคียงกับขีด จำกัด ( ARG_MAXในระบบส่วนใหญ่)

ข้อดีอีกประการของวิธีนี้คือมันทำงานได้อย่างสมบูรณ์กับชื่อไฟล์ที่มีอักขระช่องว่างทั้งหมดซึ่งไม่ใช่กรณีของลูปดั้งเดิม


17
+1สำหรับการชี้+อาร์กิวเมนต์การค้นหา ฉันคิดว่าหลายคนไม่ทราบเรื่องนี้ (ฉันไม่ได้)
gerrit

7
ไม่ใช่ทุกรุ่นที่findมี+ข้อโต้แย้ง xargsคุณจะได้รับผลกระทบเหมือนกันโดยท่อไป
Barmar

5
@Barmar +ส่วนที่ต้องการโดย POSIX ดังนั้นควรพกพา -print0ไม่ใช่
แกรม

1
ฉันยังคงพบเจอกับการใช้งานที่ไม่มี YMMV
Barmar

1
@ChrisDown สิ่งที่ฉันได้ค้นพบก็คือ Busybox findมีตัวเลือกที่ใช้ได้ แต่แค่ถือว่ามันเป็นเหมือน;ใต้พื้นผิว
แกรม

29

echoเป็นตัวเชลล์ ในทางตรงกันข้ามtouchเป็นไบนารีภายนอก

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

เชลล์บิลด์อินนั้นเร็วกว่ามากเนื่องจากไม่มีค่าใช้จ่ายในการโหลดโปรแกรมเช่นไม่มีfork/ execเกี่ยวข้อง เช่นนี้คุณจะสังเกตเห็นความแตกต่างของเวลาที่สำคัญเมื่อดำเนินการคำสั่ง buildin กับคำสั่งภายนอกจำนวนมาก

นี่คือเหตุผลที่ยูทิลิตี้เช่นtimeนั้นมีอยู่เป็นเชลล์บิลด์

คุณสามารถรับรายการทั้งหมดของเชลล์บิวด์อินได้โดยพูดว่า:

enable -p

ดังกล่าวข้างต้นโดยใช้ยูทิลิตี้เมื่อเทียบกับในตัวส่งผลให้ประสิทธิภาพการย่อยสลายอย่างมีนัยสำคัญ ต่อไปนี้เป็นสถิติของเวลาที่ใช้ในการสร้างไฟล์ ~ 9000 โดยใช้builtin 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) ดังนั้นคุณสามารถลองการทดสอบเวลาโดยใช้สิ่งนั้นแทนการติดตั้งในตัว
Michael Mrozek

@MichaelMrozek เพิ่มการทดสอบเวลาสำหรับ builtin และไบนารี
devnull
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.