Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

ประสิทธิภาพของการใช้ลูปเทียบกับตัวซ้ำ (Performance in Loops vs. Iterators)

เพื่อที่จะตัดสินใจว่าควรใช้ลูปหรือตัวซ้ำ คุณจำเป็นต้องรู้ว่าการประยุกต์ใช้งานแบบใดเร็วกว่ากัน ระหว่างฟังก์ชัน search เวอร์ชันที่ใช้ลูป for แบบชัดเจน กับเวอร์ชันที่ใช้ตัวซ้ำ

เราได้ทดสอบประสิทธิภาพ (benchmark) โดยการโหลดเนื้อหาทั้งหมดของหนังสือ The Adventures of Sherlock Holmes โดย Sir Arthur Conan Doyle เข้าไปใน String และค้นหาคำว่า the ในเนื้อหานั้น นี่คือผลการทดสอบประสิทธิภาพของฟังก์ชัน search เวอร์ชันที่ใช้ลูป for และเวอร์ชันที่ใช้ตัวซ้ำ:

test bench_search_for  ... bench:  19,620,300 ns/iter (+/- 915,700)
test bench_search_iter ... bench:  19,234,900 ns/iter (+/- 657,200)

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

สำหรับการทดสอบประสิทธิภาพที่ครอบคลุมยิ่งขึ้น คุณควรทดสอบโดยใช้ข้อความต่าง ๆ ที่มีขนาดหลากหลายเป็น contents ใช้คำต่าง ๆ และความยาวคำที่แตกต่างกันเป็น query รวมถึงรูปแบบการทดสอบอื่น ๆ อีกมากมาย แต่ประเด็นสำคัญคือ: ตัวซ้ำแม้จะเป็นนามธรรมระดับสูง (high-level abstraction) แต่ก็ถูกคอมไพล์ลงไปเป็นโค้ดในระดับเดียวกับที่คุณเขียนโค้ดระดับต่ำด้วยตัวเอง ตัวซ้ำเป็นหนึ่งในนามธรรมไร้ต้นทุน (zero-cost abstractions) ของ Rust ซึ่งหมายความว่าการใช้นามธรรมนี้จะไม่สร้างภาระส่วนเกิน (overhead) เพิ่มเติมในการทำงานขณะรันไทม์ เรื่องนี้คล้ายคลึงกับวิธีที่ Bjarne Stroustrup ผู้ออกแบบและผู้ประยุกต์ใช้งาน C++ คนแรก ได้ให้นิยามคำว่าภาระส่วนเกินเป็นศูนย์ไว้ในการนำเสนอ ETAPS keynote ปี 2012 หัวข้อ “Foundations of C++”:

โดยทั่วไปแล้ว การประยุกต์ใช้งานใน C++ จะปฏิบัติตามหลักการภาระส่วนเกินเป็นศูนย์ (zero-overhead principle): สิ่งที่คุณไม่ได้ใช้ คุณจะไม่ต้องจ่าย (ภาระส่วนเกิน) และยิ่งไปกว่านั้น: สิ่งที่คุณนำมาใช้งาน คุณก็ไม่สามารถเขียนโค้ดด้วยมือให้ดีไปกว่านี้ได้แล้ว

ในหลาย ๆ กรณี โค้ด Rust ที่ใช้ตัวซ้ำจะถูกคอมไพล์เป็นภาษาแอสเซมบลี (assembly) เดียวกับที่คุณจะเขียนด้วยมือ การปรับปรุงประสิทธิภาพ (optimizations) เช่น การคลี่ลูป (loop unrolling) และการยกเลิกการตรวจสอบขอบเขต (bounds checking) ในการเข้าถึงอาเรย์จะถูกนำมาใช้ และทำให้โค้ดผลลัพธ์มีประสิทธิภาพสูงอย่างยิ่ง ตอนนี้เมื่อคุณทราบเรื่องนี้แล้ว คุณก็สามารถใช้ตัวซ้ำและโคลเชอร์ได้อย่างหมดกังวล! พวกมันทำให้โค้ดดูเหมือนอยู่ในระดับที่สูงขึ้น แต่ไม่ได้สร้างภาระต่อประสิทธิภาพขณะรันไทม์เลย

สรุป

โคลเชอร์และตัวซ้ำเป็นฟีเจอร์ของ Rust ที่ได้รับแรงบันดาลใจมาจากแนวคิดของภาษาโปรแกรมเชิงฟังก์ชัน (functional programming languages) พวกมันมีส่วนช่วยให้ Rust มีความสามารถในการสื่อสารแนวคิดระดับสูงได้อย่างชัดเจนโดยได้ประสิทธิภาพในระดับต่ำ การประยุกต์ใช้งานโคลเชอร์และตัวซ้ำได้รับการออกแบบมาจนประสิทธิภาพขณะรันไทม์ไม่ได้รับผลกระทบใด ๆ ซึ่งเป็นส่วนหนึ่งของเป้าหมายของ Rust ที่มุ่งมั่นจะมอบนามธรรมไร้ต้นทุน (zero-cost abstractions) ให้แก่นักพัฒนา

ตอนนี้เมื่อเราได้ปรับปรุงความสามารถในการแสดงออกทางโค้ดของโปรเจกต์ I/O ของเราแล้ว เรามาดูฟีเจอร์เพิ่มเติมของ cargo ที่จะช่วยให้เราแบ่งปันโปรเจกต์กับโลกภายนอกได้กันเถอะ