การปรับปรุงประสิทธิภาพ Custom Memory Copy ยังแพ้การใช้งาน Standard Library

ทีมชุมชน BigGo
การปรับปรุงประสิทธิภาพ Custom Memory Copy ยังแพ้การใช้งาน Standard Library

การแสวงหาของนักพัฒนาเพื่อเอาชนะฟังก์ชัน memcpy มาตรฐานได้จุดประกายการอภิปรายทางเทคนิคที่น่าสนใจเกี่ยวกับการปรับปรุงประสิทธิภาพหน่วยความจำและประสิทธิผลของการใช้งานแบบกำหนดเอง การทดลองนี้เกี่ยวข้องกับการสร้างวิธีการคัดลอกหน่วยความจำเฉพาะหลายแบบโดยใช้คำสั่ง CPU ขั้นสูง แต่ผลลัพธ์เผยให้เห็นบทเรียนสำคัญเกี่ยวกับการปรับปรุงประสิทธิภาพ

ข้อมูลจำเพาะของระบบทดสอบ:

  • CPU: AMD Ryzen 7 1700X
  • RAM: 32GB DDR4 @ 2400MHz
  • ช่วงการทดสอบประสิทธิภาพ: การคัดลอกข้อมูล 32MB ถึง 64MB
  • เครื่องมือที่ใช้: Google Benchmark

Standard Library ส่งมอบประสิทธิภาพสูงสุดอยู่แล้ว

การอภิปรายในชุมชนเน้นประเด็นสำคัญเกี่ยวกับ standard library สมัยใหม่ แม้จะมีการใช้งานแบบกำหนดเองหลายแบบที่ใช้คำสั่ง SIMD, การทำ vectorization ด้วย AVX และ non-temporal moves แต่ฟังก์ชัน memcpy มาตรฐานยังคงแข่งขันได้ในกรณีการใช้งานส่วนใหญ่ สิ่งนี้ไม่น่าแปลกใจสำหรับนักพัฒนาที่มีประสบการณ์ที่เข้าใจว่าการใช้งาน standard library ได้รับการปรับปรุงมาหลายทศวรรษและปรับตัวโดยอัตโนมัติกับสถาปัตยกรรมฮาร์ดแวร์ที่แตกต่างกัน

การใช้งาน glibc ใช้เทคนิคที่ซับซ้อนรวมถึง Enhanced Rep Movsb สำหรับการคัดลอกแบบธรรมดาและคำสั่ง AVX สำหรับบล็อกหน่วยความจำที่ไม่ได้จัดตำแหน่ง มันสลับไปมาระหว่างกลยุทธ์ต่างๆ อย่างชาญฉลาดตามขนาดข้อมูลและการจัดตำแหน่ง ทำให้การใช้งานแบบกำหนดเองยากที่จะเอาชนะได้อย่างสม่ำเสมอ

วิธีการ Benchmark ทำให้เกิดคำถาม

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

benchmarks เหล่านี้ไม่เกี่ยวข้องเนื่องจาก CPU ดำเนินการคำสั่งแบบไม่เรียงลำดับ ส่วนใหญ่แล้ว cpu จะดำเนินการ assembly ต่อไปในขณะที่การดำเนินการคัดลอกกำลังดำเนินอยู่

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

กรณีเฉพาะแสดงแนวโน้มที่ดี

แม้ว่าข้อสรุปโดยรวมจะเอื้อต่อการใช้งานมาตรฐาน แต่การทดลองได้เผยให้เห็นรูปแบบที่น่าสนใจบางอย่าง สำหรับกรณีการใช้งานที่เฉพาะเจาะจงมากที่มีการจัดตำแหน่งหน่วยความจำที่ควบคุมได้และขนาดข้อมูลขนาดใหญ่ แนวทางแบบกำหนดเองบางอย่างแสดงการปรับปรุง วิธี streaming prefetch มีประสิทธิภาพดีสำหรับการคัดลอกที่ใหญ่กว่า 128MB ในขณะที่ unrolled AVX โดดเด่นในช่วงขนาดเล็กถึงกลาง

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

สรุปผลการทดสอบประสิทธิภาพ:

  • ดีที่สุดสำหรับการคัดลอกขนาดใหญ่ (128MB+): วิธี Streaming prefetch
  • ดีที่สุดสำหรับขนาดเล็ก-กลาง: Unrolled AVX
  • เสถียรที่สุด: Standard library memcpy/memmove
  • แย่ที่สุดโดยรวม: การใช้งาน Regular memmove
  • ประโยชน์จาก Unrolling: ปรับปรุงประสิทธิภาพได้ 5-10% ในกรณีส่วนใหญ่

ความกังวลด้านความปลอดภัยและความถูกต้อง

การอภิปรายยังได้สัมผัสกับข้อพิจารณาด้านความปลอดภัยที่สำคัญ คำสั่ง Non-temporal แม้ว่าจะอาจเร็วกว่า แต่ก็นำเสนอความซับซ้อนในการจัดลำดับที่สามารถนำไปสู่พฤติกรรมที่ไม่ได้กำหนดไว้หากไม่มี memory fence ที่เหมาะสม Standard library จัดการกรณีขอบเขตเหล่านี้โดยอัตโนมัติ ในขณะที่การใช้งานแบบกำหนดเองต้องการความใส่ใจอย่างระมัดระวังต่อการจัดลำดับหน่วยความจำและการจัดการการทับซ้อน

นักพัฒนาได้ตั้งชื่อไฟล์การใช้งานแบบกำหนดเองของตนเองอย่างชาญฉลาดว่า dangerous.cc พร้อมคำเตือนเกี่ยวกับปัญหาที่อาจเกิดขึ้น โดยยอมรับความเสี่ยงที่เกี่ยวข้องในการข้ามฟังก์ชัน standard library ที่ผ่านการทดสอบอย่างดี

วิธีการใช้งาน Custom Implementation ที่ทำการทดสอบ:

  • Basic SIMD MOVSD: ลูป assembly แบบ vectorized อย่างง่าย
  • Aligned AVX: การดำเนินการ vector 32-byte พร้อมข้อกำหนดการจัดตำแหน่ง
  • Stream Aligned AVX: การดำเนินการที่ข้าม cache สำหรับการคัดลอกข้อมูลขนาดใหญ่
  • Stream AVX with Prefetch: การ prefetch ข้อมูลใน cache สำหรับข้อมูลในการทำซ้ำครั้งถัดไป
  • เวอร์ชัน Unrolled: การคลาย loop ด้วยตัวคูณ 4x เพื่อลดการแยกสาขา

คำตัดสินเกี่ยวกับการปรับปรุงประสิทธิภาพหน่วยความจำ

การทดลองนี้เป็นเครื่องเตือนใจที่มีค่าว่าการปรับปรุงประสิทธิภาพไม่ได้เกี่ยวกับการเขียนโค้ดแบบกำหนดเองเสมอไป Standard library สมัยใหม่แสดงถึงงานปรับปรุงหลายทศวรรษโดยผู้เชี่ยวชาญที่เข้าใจทั้งความสามารถของฮาร์ดแวร์และข้อกำหนดที่ละเอียดอ่อนของการจัดการหน่วยความจำที่ถูกต้อง แม้ว่าการสำรวจการใช้งานแบบกำหนดเองอาจเป็นการศึกษา แต่คำแนะนำเชิงปฏิบัติยังคงชัดเจน: ยึดติดกับฟังก์ชัน standard library เว้นแต่คุณจะมีข้อกำหนดที่เฉพาะเจาะจงมากและความเชี่ยวชาญในการจัดการความเสี่ยงที่เกี่ยวข้อง

SIMD: Single Instruction, Multiple Data - ประเภทของการประมวลผลแบบขนานที่ดำเนินการเดียวกันกับจุดข้อมูลหลายจุดพร้อมกัน

AVX: Advanced Vector Extensions - ชุดคำสั่ง CPU ที่เปิดใช้งานการดำเนินการแบบ vectorized บนชิ้นข้อมูลที่ใหญ่กว่า

คำสั่ง Non-temporal: คำสั่ง CPU ที่ข้ามหน่วยความจำแคชสำหรับการดำเนินการบางอย่าง

อ้างอิง: Going faster than memcpy