ไม่มีใครไม่ชอบโค้ดที่ทำงานไว ๆ และยิ่งถ้าเขียนสั้นลงได้ก็ยิ่งดี แต่ถ้าเขียนยาวขึ้นแล้วทำงานเร็วขึ้นละ มันเป็นไปได้อย ่างไร แล้วมันดีจริงไหม?
ก่อนอื่นสำหรับคนที่ลืมว่า for-loop เขียนแบบไหน แล้วทำงานอย่างไรไปแล้ว หรือไม่คุ้นชินกับรูปแบบนี้ ก็มาหยุดพักชมตรงนี้สักครู่
ใน for-loop รูปแบบนี้ภายในจะแบ่งเป็น 3 ส่วนคั่นด้วย Semicolon ( ; ) เรียงตามลำดับดังนี้
- สิ่งที่ทำก่อนจะเริ่มต้นลูป ส่วนมากจะประกาศตัวแปรไว้นับรอบการทำงานของลูป
- เงื่อนไขในการทำงานของลูป ก่อนจะทำงานแต่ละรอบจะมาตรวจสอบตรงนี้ หากเป็น true ก็จะเริ่มรอบต่อไป แต่หากเป็น false จะเป็นการจบการทำงานของลูปนั้น
- จัดการการนับในตัวแปรที่ประกาศไว้ในขั้นที่ 1 ว่าจบแต่ละรอบให้เพิ่ม/ลดเท่าไร
มาลองเทียบดูกันชัด ๆ ว่าแบบที่บอกว่าเขียนยาวกว่าแต่ว่าทำงานได้ไวกว่ามาเป็นอย่างไร
แบบแรกมองผ่าน ๆ ก็คงไม่ได้ดูแปลกตานัก เหมือนกับการลูปเข้าไปใน Array ทั่วไป แต่ถ้าพิจารณาดูดี ๆ จะเห็นว่าในส่วนเงื่อนไขมีการเรียก length เพื่ อนำจำนวนใน Array นั้น ซึ่งเงื่อนไขนั้นจะถูกตรวจสอบทุกครั้งก่อนเริ่มแต่ละรอบ ทำให้ต้องเข้าไปนับจำนวนทุกรอบโดยไม่จำเป็น ในกรณีที่ Array ไม่มีการเปลี่ยนแปลงภายในลูป
เมื่อเห็นแบบนี้แล้วเลยก็กลายมาเป็นแบบที่สอง ถ้าไม่อยากให้มันนับซ้ำ ๆ ก็นับรอไว้ก่อนสิ ยาวขึ้นมาหนึ่งบรรทัด แต่ว่าไม่ต้องเข้าไปนับใหม่ทุกรอบให้เสียเวลา
ได้วิธีเขียนแบบใหม่ที่ยังทำให้เร็วขึ้นและน่าสนใจแล้ว แต่ก่อนที่จะนำแบบนี้ไปใช้จริง ให้ลองกลับมาคิดอีกรอบว่ามันเร็วขึ้นจริงไหม?
แต่ตอนใช้งานจริง มันก็ต้องมีปัจจัยอื่นเข้ามาร่วมด้วยอยู่แล้ว อย่างเช่นในกรณีนี้ที่โค้ดเราเป็น JavaScript แล้วไปทำงานในเอนจิน V8 ที่ถูกใช้โดย Node.js หรือ Chromium-based Browser เช่น Google Chrome หรือ Microsoft Edge
ซึ่งหนึ่งในการทำงานของ V8 ก็คือทุกครั้งที่มีการยุ่งกับ Array เช่น push, pop เพื่อเพิ่ม/ลบข้อมูล จะทำการคำนวณขนาดไว้ให้ทุกครั้ง ดังนั้นการเรียก length แต่ละรอบก็จะไม่ได ้เป็นการสั่งให้เข้าไปไล่นับจำนวนทุก ๆ รอบอย่างที่คิดกันไว้
ทำให้ความเร็วในการทำงานของทั้งสองแบบไม่ได้ต่างกันมากจนเห็นข้อแตกต่าง ดังนั้นให้เลือกใช้แบบที่สะดวกหรืออ่านง่ายสำหรับเราจะดีที่สุด
อีกกรณีหนึ่งที่น่าสนใจก็คือหากเราเก็บข้อมูลไว้ใน ArrayList ของ Java หรือ Collection ของ C# หากไปแกะถึงในซอร์สโค้ดของทั้งสองตัวนี้จะเห็นได้ว่าเวลาดึงจำนวนออกมาไม่ได้เป็นการเข้าไปไล ่นับแต่อย่างใด แต่ว่าแค่อ่านค่ามาให้เราเฉย ๆ เพราะมีการเก็บจำนวนไว้ภายในอยู่แล้ว จะเพิ่มลดก็ต่อเมื่อมีการเรียก Add หรือ Remove เท่านั้น
เป็นกรณีที่คล้ายกับ JavaScript ที่เจาะจงกับเอนจินที่นำโค้ดไปทำงาน แต่ว่ากรณีนี้เป็นที่ตัวภาษาเลย ซึ่งก็แปลว่าไม่ว่าจะนำไปทำงานที่ไหนก็จะได้ผลลัพธ์ที่คล้ายกันแน่นอน
สรุปแล้วมันควรใช้ไหมเนี่ย? ตรงนี้ก็ขึ้นอยู่กับดุลย์พินิจของตัวเราเอง แต่ว่าก็อย่าพยา ยามไปเพิ่มประสิทธิภาพไปเสียทุกจุด
“Premature-micro optimizations are the root of all evil”
“การพยายามเพิ่มประสิทธิภาพอย่างไม่ถูกที่ถูกเวลาจะเป็นต้นตอของปัญหาทั้งหมด”
การเพิ่มประสิทธิภาพไม่ใช้สิ่งที่ผิด แต่ก่อนจะทำลองมาตอบคำถามเหล่านี้ดูก่อนดีไหม?
- ถ้าเร็วขึ้นนิดเดียวไม่ได้แตกต่างจนสังเกตได้ เลือกที่อ่านง่ายกว่าดีไหม?
- ความเร็วเป็นจุดประสงค์หลักไหม? ถ้าไม่ใช่ค่อยกลับมาทำทีหลังก็ได้
- หากไม่ทำตอนนี้จะเกิดปัญหาตามมาตอนหลังไหม? กลับมาแก้ยากไหม?
- ตอนทำงานจริง ๆ มันมีการจัดการพวกนี้ให้แล้วหรือเปล่า?
รู้จักเลือกปรับประสิทธิภาพให้ถูกที่ถูกเวลา ทำเมื่อเหมาะสม เพียงเท่านี้เราก็จะเขียนโค้ดได้อย่างสบายใจ ไม่ต้องกังวลกับเรื่องเล็กน้อยในโค้ดที่ไม่จำเป็นอีกต่อไป
อ่านเพิ่มเติม/อ้างอิง
- https://stackoverflow.com/a/5753407
- https://hg.openjdk.java.net/jdk8/jdk8/jdk/file/tip/src/share/classes/java/util/ArrayList.java
- https://referencesource.microsoft.com/#mscorlib/system/collections/objectmodel/collection.cs
เนื้อหานี้ถูกเผยแพร่ครั้งแรกในรูปแบบของโพสท์ Facebook เพื่อให้ง่ายต่อการสืบค้นจึงนำมาจัดเก็บในรูปแบบบทความด้วย