Bash script; การเพิ่มประสิทธิภาพความเร็วการประมวลผล


10

ฉันสงสัยว่ามีแนวทางทั่วไปในการเพิ่มประสิทธิภาพสคริปต์ Bash หรือไม่

  • ตัวอย่างเช่นสะดวกในการเขียนลูปมากกว่าบรรทัดคำสั่ง แต่มันเร็วกว่าในการประมวลผลสำหรับระบบหรือไม่ ตัวอย่าง:

    for i in a b c; do echo $i; done
    
    echo a
    echo b
    echo c
  • บางครั้งผู้คนนำเสนอวิธีแก้ไขปัญหาต่าง ๆ สำหรับปัญหาเดียวกัน ตัวอย่างเช่นsed, cut, awkและechoมีความสามารถทั้งหมดที่จะตัดตัวเลขจากสตริง ฉันสงสัยว่าถ้าคุณสามารถบอกได้ว่ายิ่งมีจำนวนรหัสน้อยเท่าไหร่ก็ยิ่งเร็วเท่านั้นหากคุณใช้:

    1. คำสั่งเดียวกันเช่น

      STRING=abc.def
      echo ${STRING} | sed 's/.def//g'
      echo ${STRING} | sed '$s/....$//'
    2. คำสั่งต่าง ๆ เช่น

      STRING=abc.def
      echo ${STRING} | cut -d . -f 1
      echo ${STRING} | sed 's/.def//g'

2
ในกรณีส่วนใหญ่ฉันต้องการอ่านมากกว่าความเร็ว
Bernhard

1
a) ใช่มีโอเวอร์เฮดในการใช้ for for loop แต่มันไม่สำคัญ b) พยายามลดคำสั่งภายนอกและลดสิ่งต่าง ๆ จากภายใน bash และถ้าคุณต้องปรับเชลล์สคริปต์ให้เหมาะสมคุณกำลังทำอะไรผิดพลาดและควรพิจารณาใช้ภาษาที่มีการสนับสนุน profiler ที่ดีกว่า
Ulrich Dangel

การแสดงออกปกติโดยทั่วไปแล้วจะมีประสิทธิภาพที่ต่ำกว่าตัวเลือกอื่น ๆ ดังนั้นหากคุณเห็นวิธีที่จะทำอะไรบางอย่างโดยไม่มีใครสักคน (เช่นตัด) ให้ใช้วิธีนั้น แน่นอนว่าอาจส่งผลต่อความสามารถในการอ่านหากความสามารถในการอ่านของคุณ จำกัด เฉพาะการแสดงออกปกติ)
goldilocks

คำตอบ:


8

เชลล์ไม่ได้ทำการปรับโครงสร้างโค้ดที่ส่งมอบมันจะตีความเพียงหนึ่งบรรทัดหลังจากที่อื่น (ไม่มีอะไรที่สมเหตุสมผลในล่ามคำสั่ง) เวลาส่วนใหญ่ที่เชลล์ใช้ไปนั้นเป็นการวิเคราะห์คำศัพท์ / การวิเคราะห์คำ / เรียกใช้งานโปรแกรมที่เรียกว่า

สำหรับการดำเนินการอย่างง่าย ๆ (อย่างเช่นข้อความที่ลงท้ายด้วยตัวอย่างในตอนท้ายของคำถาม) ฉันจะประหลาดใจถ้าเวลาในการโหลดโปรแกรมนั้นไม่ทำให้ความแตกต่างของความเร็วต่ำลง

คุณธรรมของเรื่องราวคือถ้าคุณต้องการความเร็วมากขึ้นคุณจะดีกว่าด้วยภาษาที่รวบรวม (กึ่ง) เช่น Perl หรือ Python ซึ่งเร็วกว่าที่จะเริ่มต้นด้วยซึ่งคุณสามารถเขียนการดำเนินการต่างๆที่กล่าวถึงโดยตรง และไม่ต้องโทรออกไปยังโปรแกรมภายนอกและมีตัวเลือกในการเรียกใช้โปรแกรมภายนอกหรือโทรเข้าโมดูล C (หรืออะไรก็ตาม) ที่ได้รับการปรับปรุงเพื่อทำงานส่วนใหญ่ นั่นคือเหตุผลว่าทำไมใน Fedora "การบริหารระบบน้ำตาล" (GUI, เป็นหลัก) เขียนใน Python: สามารถเพิ่ม GUI ที่ดีโดยไม่ต้องใช้ความพยายามมากเกินไปเร็วพอสำหรับแอพพลิเคชั่นดังกล่าวสามารถเข้าถึงการโทรของระบบได้โดยตรง หากความเร็วไม่เพียงพอให้คว้า C ++ หรือ C

แต่อย่าไปที่นั่นเว้นแต่คุณสามารถพิสูจน์ได้ว่าการเพิ่มประสิทธิภาพนั้นคุ้มค่ากับการสูญเสียความยืดหยุ่นและเวลาในการพัฒนา Shell สคริปต์ไม่ได้เลวร้ายเกินไปที่จะอ่าน แต่ฉันตัวสั่นเมื่อฉันจำบางสคริปต์ที่ใช้ในการติดตั้ง Ultrix ฉันเคยพยายามถอดรหัส ฉันเลิกใช้ "การเพิ่มประสิทธิภาพสคริปต์เชลล์" มากเกินไป


1
+1 แต่ผู้คนจำนวนมากจะโต้แย้งว่ามีแนวโน้มที่จะได้รับความยืดหยุ่นและเวลาในการพัฒนาโดยใช้บางอย่างเช่น python หรือ perl vs. shell ไม่ใช่การสูญเสีย ฉันจะบอกว่าใช้เชลล์สคริปต์เฉพาะเมื่อจำเป็นเท่านั้นหรือสิ่งที่คุณทำเกี่ยวข้องกับคำสั่งเชลล์เฉพาะจำนวนมาก
goldilocks

22

กฎข้อแรกของการเพิ่มประสิทธิภาพคือไม่ได้เพิ่มประสิทธิภาพ ทดสอบก่อน หากการทดสอบแสดงว่าโปรแกรมของคุณช้าเกินไปให้มองหาการปรับให้เหมาะสมที่สุด

วิธีเดียวที่จะตรวจสอบให้แน่ใจคือการใช้มาตรฐานสำหรับกรณีการใช้งานของคุณ มีกฎทั่วไปอยู่บ้าง แต่จะใช้กับปริมาณข้อมูลทั่วไปในแอปพลิเคชันทั่วไปเท่านั้น

กฎทั่วไปบางข้อซึ่งอาจเป็นจริงหรือไม่ก็ได้ในบางกรณี:

  • สำหรับการประมวลผลภายในในเชลล์ ATT ksh นั้นเร็วที่สุด หากคุณทำการปรับแต่งสตริงจำนวนมากให้ใช้ ATT ksh ขีดมาเป็นอันดับสอง bash, pdksh และ zsh จะล้าหลัง
  • หากคุณจำเป็นต้องเรียกใช้เชลล์บ่อยครั้งเพื่อทำงานสั้น ๆ ในแต่ละครั้ง dash จะชนะเนื่องจากเวลาเริ่มต้นต่ำ
  • การเริ่มต้นกระบวนการภายนอกต้องเสียเวลาดังนั้นจึงเร็วกว่าที่จะมีไพพ์ไลน์เดียวที่มีส่วนที่ซับซ้อนกว่าไพพ์ไลน์ในลูป
  • echo $fooช้ากว่าecho "$foo"เนื่องจากไม่มีเครื่องหมายอัญประกาศคู่จึงแยก$fooเป็นคำและตีความแต่ละคำว่าเป็นรูปแบบสัญลักษณ์แทนชื่อไฟล์ ที่สำคัญกว่านั้นคือพฤติกรรมที่แยกและกลมกลืนนั้นไม่ค่อยเป็นที่ต้องการ ดังนั้นอย่าลืมคำพูดคู่ใส่เสมอ ๆ "$foo"แทนตัวแปรและแทนคำสั่ง"$(foo)",
  • เครื่องมือเฉพาะมักจะชนะเหนือเครื่องมือเอนกประสงค์ ตัวอย่างเช่นเครื่องมือเช่นcutหรือheadสามารถเลียนแบบด้วยsedแต่sedจะช้าลงและawkจะช้าลง การประมวลผลสตริงของเชลล์ทำงานช้า แต่สำหรับสตริงสั้น ๆ นั้นส่วนใหญ่จะเป็นการเรียกโปรแกรมภายนอก
  • ภาษาขั้นสูงอื่น ๆ เช่น Perl, Python และ Ruby มักจะให้คุณเขียนอัลกอริทึมที่เร็วกว่า แต่มีเวลาเริ่มต้นที่สูงขึ้นอย่างมากดังนั้นพวกเขาจึงคุ้มค่ากับประสิทธิภาพสำหรับข้อมูลจำนวนมากเท่านั้น
  • อย่างน้อยใน Linux, ไปป์มักจะเร็วกว่าไฟล์ชั่วคราว
  • การใช้เชลล์สคริปต์ส่วนใหญ่อยู่ในกระบวนการ I / O-bound ดังนั้นการใช้ CPU จึงไม่สำคัญ

หายากที่ประสิทธิภาพเป็นข้อกังวลในเชลล์สคริปต์ รายการข้างต้นเป็นตัวบ่งชี้อย่างหมดจด; เป็นเรื่องปกติที่จะใช้วิธี“ ช้า” อย่างสมบูรณ์แบบในกรณีส่วนใหญ่เนื่องจากความแตกต่างมักจะเป็นเศษส่วนของเปอร์เซ็นต์

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


2
ในขณะที่pythonและrubyเป็นมั่นเหมาะช้าที่จะเริ่มต้นอย่างน้อยในระบบของฉันperlเป็นที่รวดเร็วในการเริ่มต้นหรือbash kshGNU awk ช้ากว่า GNU อย่างมากโดยเฉพาะในสถานที่ utf-8 แต่มันไม่เป็นความจริงสำหรับ awks และ seds ทั้งหมด ksh93> dash> pdksh> zsh> bash นั้นไม่ได้มีความคมชัดเหมือนเช่นเคย กระสุนบางนัดดีกว่าบางอย่างและผู้ชนะไม่เหมือนกันเสมอไป
Stéphane Chazelas

2
เรื่อง"คุณต้องได้รับมากจาก ... " : ถ้า"คุณ"มีฐานผู้ใช้จริง ด้วยเชลล์สคริปต์ในแพ็คเกจลีนุกซ์ยอดนิยมผู้ใช้มักจะเสียเวลารวมกันหลายคำสั่งมากกว่าเวลาที่โปรแกรมเมอร์รีบเร่ง
agc

2

เราจะขยายที่นี่ในตัวอย่างแบบวงกลมของเราด้านบนเพื่อแสดงคุณสมบัติด้านประสิทธิภาพของตัวแปลสคริปต์เชลล์ การเปรียบเทียบbashและdashล่ามสำหรับตัวอย่างนี้ที่กระบวนการเกิดขึ้นสำหรับแต่ละไฟล์ 30,000 ไฟล์แสดงว่าเส้นประสามารถแยกwcกระบวนการเกือบสองเท่าเร็วbash

bash-4.2$ time dash -c 'for i in *; do wc -l "$i"; done>/dev/null'
real    0m1.238s
user    0m0.309s
sys     0m0.815s


bash-4.2$ time bash -c 'for i in *; do wc -l "$i"; done>/dev/null'
real    0m1.422s
user    0m0.349s
sys     0m0.940s

การเปรียบเทียบความเร็วในการวนรอบฐานโดยไม่เรียกใช้wcกระบวนการแสดงให้เห็นว่าการวนซ้ำของเส้นประนั้นเร็วขึ้นเกือบ 6 เท่า!

$ time bash -c 'for i in *; do echo "$i">/dev/null; done'
real    0m1.715s
user    0m1.459s
sys     0m0.252s



$ time dash -c 'for i in *; do echo "$i">/dev/null; done'
real    0m0.375s
user    0m0.169s
sys     0m0.203s

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

$ time find -type f -print0 | wc -l --files0-from=- | tail -n1
    30000 total
real    0m0.299s
user    0m0.072s
sys     0m0.221s

ข้างต้นเป็นวิธีการแก้ปัญหาที่มีประสิทธิภาพมากที่สุดและแสดงให้เห็นถึงจุดที่ควรทำอย่างน้อยที่สุดเท่าที่จะทำได้ในเชลล์สคริปต์และตั้งเป้าที่จะใช้มันเพื่อเชื่อมต่อลอจิกที่มีอยู่ในชุดยูทิลิตีมากมาย

ข้อผิดพลาดของสคริปต์ที่ถูกขโมยมาจากเชลล์ทั่วไปโดยPádraig Brady


1
กฎทั่วไป: การจัดการตัวอธิบายไฟล์ก็มีค่าใช้จ่ายด้วยดังนั้นควรลดจำนวนลง แทนการทำดีfor i in *; do wc -l "$i">/dev/null; done for i in *; do wc -l "$i"; done>/dev/null
จัดการ

@ การผลิตมันจะเป็นโมฆะของtimecmd
Rahul Patil

@ การจัดการที่ดี ... ตอนนี้โปรดส่งฉันโดยไม่ต้องเรียกwc -lตรวจสอบฉันได้ปรับปรุงในโพสต์เอาท์พุทของคุณ
Rahul Patil

การวัดก่อนหน้านี้ทำในไดเรกทอรีที่เล็กกว่า ตอนนี้ฉันสร้างไฟล์หนึ่งไฟล์ด้วย 30,000 ไฟล์และทำการทดสอบซ้ำ: pastebin.com/pCV6QKp2
จัดการเอกสาร

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