เจาะลึก SearchValues<T> ใน .NET 8 เมื่อไหร่ควรใช้และเมื่อไหร่ควรหลีกเลี่ยง
นักพัฒนาแชร์ผลทดสอบจริงหลังใช้งาน SearchValues<T> เผยตัวเลขประสิทธิภาพที่ต่างกันลิบลับระหว่างการตั้งค่าแบบ Static และการสร้างใหม่ทุกครั้งที่เรียกใช้งาน

ภาพประกอบจากคลังภาพสต็อก ไม่ใช่ภาพจากเหตุการณ์จริง
- SearchValues
เปิดตัวใน .NET 8 ช่วยเก็บชุดค่าคงที่สำหรับค้นหาและรันไทม์เลือกกลยุทธ์ที่เร็วที่สุด - ผลเบนช์มาร์กกรณีเดือมหนาแน่นแทบไม่ต่างจาก IndexOfAny เดิม แต่หากเดือมพบน้อยจะเร็วกว่าประมาณ 1.7 เท่า
- การสร้าง SearchValues ภายในเมธอดต่อการเรียกใช้งานจะช้ากว่าการเก็บไว้ใน static readonly เกือบ 3 เท่า
หลายคนมักมองว่าเครื่องมือใหม่อย่าง SearchValues<T> ที่เพิ่มเข้ามาใน .NET 8 เปรียบเสมือนสูตรโกงที่ช่วยให้การสแกนสตริงเร็วขึ้นถึงห้าเท่า จนชวนให้อยากรีแฟกเตอร์โค้ดเดิมทันที แต่สำหรับนักพัฒนาที่ดูแลระบบจัดการล็อกซึ่งพึ่งพา IndexOfAny เป็นหลัก การทดสอบวัดประสิทธิภาพก่อนลงมือจริงจึงเป็นสิ่งจำเป็นอันดับแรก
จากการทดสอบเปรียบเทียบในกรณีที่บัฟเฟอร์ล็อกมีตัวคั่นความถี่หนาแน่นทุกๆ 60 ตัวอักษร ผลลัพธ์ที่ได้ค่อนข้างน่าประหลาดใจเนื่องจากแทบไม่ต่างกันมากนักระหว่างวิธีดั้งเดิมและเครื่องมือใหม่:
เหตุผลที่เป็นเช่นนี้เพราะเมธอด IndexOfAny บน .NET ยุคใหม่ที่มีชุดตัวอักษรขนาดเล็กได้รับการปรับแต่งแบบ vectorized มาเป็นอย่างดีอยู่แล้ว ในทางกลับกัน การเขียนลูปวนเช็คด้วยมือแบบดั้งเดิมกลับใช้เวลาช้ากว่าวิธีอื่นถึงสองเท่าตัว ซึ่งสะท้อนให้เห็นว่าปัญหาคอขวดที่แท้จริงมักมาจากโค้ดลูปที่เราเขียนขึ้นเองมากกว่าตัว API มาตรฐาน
อย่างไรก็ตาม สถานการณ์จะเปลี่ยนไปเมื่อปรับเปลี่ยนรูปแบบข้อมูลให้มีตัวคั่นที่พบน้อยลง หรือเฉลี่ยหนึ่งครั้งทุกๆ 40 กิโลไบต์ ซึ่งเป็นกรณีจำลองสำหรับการสแกนหาความผิดปกติ โดยผลลัพธ์การทดสอบแสดงให้เห็นความแตกต่างอย่างชัดเจน:
- IndexOfAny(char[]): ใช้เวลา 5.6 มิลลิวินาที (พบการตรงกัน 723 รายการ)
- SearchValues<char>: ใช้เวลา 3.3 มิลลิวินาที (พบการตรงกัน 723 รายการ)
ในกรณีที่อัตราการพบเป้าหมายค่อนข้างน้อย SearchValues จะสามารถทำงานได้อย่างเต็มประสิทธิภาพในการกวาดผ่านช่วงข้อมูลยาวๆ ที่ไม่มีอะไรเลยด้วยขั้นตอนแบบ vectorized ที่กว้างขึ้น
แนวคิดเบื้องหลัง SearchValues<T> คือการย้ายภาระการเตรียมข้อมูล (Precomputation) ไปไว้ล่วงหน้า เพื่อให้รันไทม์ไม่ต้องคำนวณซ้ำทุกครั้งที่ทำการค้นหา การเข้าใจสถาปัตยกรรมการทำงานเบื้องหลังเช่น Vectorization ช่วยให้นักพัฒนาเลือกใช้ API ได้ตรงตามลักษณะของข้อมูล ไม่ว่าจะเป็นข้อมูลที่มีรูปแบบหนาแน่นหรือกระจัดกระจาย ซึ่งส่งผลต่อประสิทธิภาพโดยรวมของแอปพลิเคชันอย่างมีนัยสำคัญ
อีกหนึ่งจุดสำคัญที่นักพัฒนาต้องระมัดระวังอย่างยิ่งคือต้นทุนในการสร้างออบเจกต์ หากมีการสร้าง SearchValues ขึ้นใหม่ภายในเมธอดทุกครั้งที่มีการเรียกใช้งาน แทนที่จะเก็บไว้ในตัวแปรแบบ static readonly ผลลัพธ์การทดสอบกับขข้อมูลหนึ่งล้านบรรทัดสั้นๆ แสดงให้เห็นภาพที่ชัดเจน:
- Static SearchValues: ใช้เวลา 25.1 มิลลิวินาที
- SearchValues per call: ใช้เวลา 70.2 มิลลิวินาที
การสร้างออบเจกต์ใหม่ต่อการเรียกใช้งานหนึ่งครั้งทำให้ประสิทธิภาพตกลงไปเกือบสามเท่า ซึ่งช้ากว่าการไม่ใช้งานเลยเสียอีก หากไม่ได้ประกาศไว้ในตัวแปรแบบสถิต ก็เท่ากับว่าเรากำลังสร้างโค้ดที่ทำให้ระบบทำงานช้าลงโดยไม่จำเป็น

ภาพประกอบจากคลังภาพสต็อก ไม่ใช่ภาพจากเหตุการณ์จริง
ที่มา: Dev.to
พบข้อมูลผิดพลาดในบทความนี้? แจ้งปัญหาบทความนี้
ความคิดเห็น
แสดงความคิดเห็น