1 คะแนน โดย GN⁺ 2025-05-11 | 1 ความคิดเห็น | แชร์ทาง WhatsApp
  • Sep เวอร์ชันล่าสุด 0.10.0 ทำความเร็วการแยกวิเคราะห์ CSV ได้สูงถึง 21 GB/s บน AMD 9950X
  • ประสิทธิภาพดีขึ้นอย่างมากจากการรองรับ AVX-512 และการแก้ปัญหาเกี่ยวกับ mask register
  • ตัวแยกวิเคราะห์ AVX-512-to-256 แบบใหม่ให้ผลลัพธ์เหนือกว่า AVX2 และตัวแยกวิเคราะห์ AVX-512 แบบเดิม
  • ในสภาพแวดล้อมแบบ มัลติเธรด สามารถประมวลผล 1 ล้านแถวได้ใน 72ms พร้อมแบนด์วิดท์ 8 GB/s
  • ด้วย การปรับแต่งซอฟต์แวร์และฮาร์ดแวร์อย่างต่อเนื่อง ทำให้ประสิทธิภาพดีขึ้นราว 3 เท่าในช่วง 2 ปี

ภาพรวมการออก Sep 0.10.0 และการเพิ่มขึ้นของประสิทธิภาพ

  • Sep ได้ทำการปรับแต่งสำหรับ CPU ที่รองรับ AVX-512 (เช่น AMD 9950X, Zen 5) ในรีลีส 0.10.0 ล่าสุด และมีการอัปเดตผลเบนช์มาร์ก
  • ในเวอร์ชันล่าสุด ทำผลงานที่โดดเด่นด้วยความเร็ว 21 GB/s ในการแยกวิเคราะห์ CSV ระดับล่าง
  • เป็นการเพิ่มประสิทธิภาพอย่างชัดเจนเมื่อเทียบกับเวอร์ชันก่อนหน้าที่ทำได้ 18 GB/s
  • รายละเอียดการปรับปรุงสามารถดูได้จาก GitHub release และ README ของ Sep
  • เนื้อหานี้อธิบายกระบวนการเพิ่มประสิทธิภาพผ่าน โค้ด C# ที่อาศัย SIMD และแอสเซมบลี x64 SIMD ซึ่งใช้เพื่อหลีกเลี่ยงความไม่มีประสิทธิภาพของ machine code แบบ AVX-512 ใน .NET 9.0

เส้นทางการพัฒนาประสิทธิภาพของ Sep

  • มีการนำเสนอภาพรวมเชิงภาพตั้งแต่ Sep รุ่นแรก 0.1.0 ถึง 0.10.0, จาก .NET 7.0 ถึง 9.0 และจาก AMD 5950X (Zen 3) ไปสู่ 9950X (Zen 5)
  • เบนช์มาร์กอ้างอิงจากการทำงานแบบเธรดเดียว และอาจมีความผันผวนเล็กน้อยในแต่ละรีลีส
  • ประเด็นสำคัญ คือทั้งการรีแฟกเตอร์ขนาดใหญ่ (การเขียนโครงสร้างภายในใหม่ใน 0.2.0) และการปรับปรุงเล็ก ๆ ที่สะสมต่อเนื่อง ล้วนช่วยผลักดันประสิทธิภาพให้ดีขึ้นอย่างสม่ำเสมอ
  • ด้วย การพัฒนาร่วมกันของฮาร์ดแวร์และซอฟต์แวร์ ทำให้บรรลุความเร็วแยกวิเคราะห์ 21 GB/s ซึ่งดีขึ้นราว 3 เท่าในเวลาเฉลี่ย 2 ปี
  • เพียงการเปลี่ยนรุ่นฮาร์ดแวร์ (5950X→9950X, 4.9→5.7GHz) ก็เป็นเหตุผลรองรับการเพิ่มขึ้นมากกว่า 1.2x แล้ว

การสร้างโค้ด AVX-512 และปัญหา mask register

  • Sep รองรับ AVX-512 มาตั้งแต่ 0.2.3 แต่มีข้อจำกัดในการใช้งาน mask register (k1-k8) ของ AVX-512
  • ใน .NET 8 ยังไม่มีการรองรับ mask register โดยตรง ทำให้ต้องคัดลอกและแปลงข้อมูลซ้ำ ๆ ระหว่างรีจิสเตอร์ทั่วไป ซึ่งกลายเป็นปัจจัยที่ทำให้ประสิทธิภาพลดลง
  • ในช่วงแรกที่ยังไม่มี CPU ที่รองรับ AVX-512 โดยเฉพาะ ผู้พัฒนาได้ทดสอบแบบจำกัดบน Xeon Silver 4316 และยืนยันว่าเร็วที่สุด

การอัปเกรดเป็น 9950X และการเปรียบเทียบ AVX-512 กับ AVX2

  • เมื่อไม่นานมานี้มีการอัปเกรด CPU จาก Zen 3 (5950X) เป็น Zen 5 (9950X) และเมื่อรันเบนช์มาร์กของ Sep ก็ทำได้ถึง 18 GB/s
  • ผลการทดลองเปรียบเทียบตัวแยกวิเคราะห์ AVX-512 กับ AVX2 โดยตรงพบว่า AVX2 ทำได้ ราว 20 GB/s และเร็วกว่า AVX-512 ประมาณ 10% อย่างน่าประหลาดใจเล็กน้อย
  • สิ่งนี้ชี้ให้เห็นว่าความไม่มีประสิทธิภาพของ การจัดการ mask register ใน .NET JIT ยังคงเป็นปัญหาอยู่

โค้ดตัวแยกวิเคราะห์ การวิเคราะห์แอสเซมบลี และตัวแยกวิเคราะห์ AVX-512-to-256 แบบใหม่

  • ตัวแยกวิเคราะห์ทั้งหมดของ Sep ประมวลผล char span ทีละ 16K และใช้การเปรียบเทียบที่อิงกับรีจิสเตอร์ SIMD (เช่น Vector256)
  • ใช้ SIMD เพื่อตรวจจับอักขระพิเศษอย่างรวดเร็ว เช่น ตัวขึ้นบรรทัดใหม่ เครื่องหมายอัญประกาศ และตัวคั่น แล้วแปลงเป็น bitmask เพื่อให้การดำเนินการแบบเซ็ตมีประสิทธิภาพสูงขึ้น
  • ตัวแยกวิเคราะห์ที่อิง AVX-512 มีโอเปอเรชันส่วนเกินจำนวนมาก เนื่องจากต้องย้ายข้อมูลซ้ำไปมาระหว่าง mask register (เช่น k1) กับรีจิสเตอร์ทั่วไป (เช่น zmm)
  • ใน 0.10.0 มีการเลื่อนการเรียก MoveMask ให้เร็วขึ้น เพื่อลด การแปลง mask ที่ไม่จำเป็น และลดจำนวนคำสั่งแอสเซมบลี
  • ตัวแยกวิเคราะห์ AVX2 ไม่มี mask register จึงมีโครงสร้างที่เรียบง่ายกว่ามาก และในทางปฏิบัติจึงเร็วกว่า AVX-512
  • ตัวแยกวิเคราะห์ AVX-512-to-256 แบบใหม่อ่านข้อมูลด้วย AVX-512 แล้วใช้คำสั่งแปลงเป็น 256 บิตเพื่อหลบเลี่ยงปัญหาการจัดการ mask โดยตรง ทำให้การอิมพลีเมนต์กระชับขึ้นและทำความเร็วได้เกิน 21 GB/s

สรุปเบนช์มาร์กของตัวแยกวิเคราะห์หลายแบบ

  • จากการเปรียบเทียบเบนช์มาร์กของตัวแยกวิเคราะห์ทุกประเภทผ่านตัวแปรสภาพแวดล้อม พบว่า ตัวแยกวิเคราะห์ AVX-512-to-256 เร็วที่สุดที่ 21.5 GB/s
  • ตัวแยกวิเคราะห์ที่อิง AVX2 และ Vector256 ก็ทำผลงานใกล้เคียงกันมาก โดยต่างกันไม่เกิน 5%
  • ตัวแยกวิเคราะห์ที่อิง Vector128 และ Vector512 ช้ากว่า AVX2 ราว 5~10% โดยเฉพาะ Vector512 ที่ช้ากว่า Vector128 เสียอีก
  • ตัวแยกวิเคราะห์ IndexOfAny ช้ากว่าตัวแยกวิเคราะห์ SIMD อื่นอย่างเห็นได้ชัด ส่วน Vector64 ไม่มีการเร่งความเร็วบน 9950X จึงให้ประสิทธิภาพต่ำมาก
  • ตัวแยกวิเคราะห์ SIMD ที่อิง AVX-512 และ AVX2 พิสูจน์ให้เห็นถึงประสิทธิภาพที่เหนือกว่าตัวแยกวิเคราะห์ CSV ประเภทเดียวกันอย่างชัดเจน

เบนช์มาร์กระดับบน: เปรียบเทียบ 5950X กับ 9950X

  • อ้างอิงจากข้อมูล 1 ล้านแถวของ package asset, Sep_MT ทำเวลาได้ 72ms (8GB/s) บน 9950X และ 119ms (4.9GB/s) บน 5950X
  • แม้กับข้อมูลโหลดจริง (เช่น float) ก็ยังทำแบนด์วิดท์แบบมัลติเธรดได้ ~8GB/s บน 9950X
  • การเปลี่ยนผ่านรุ่น (5950X→9950X) ให้ผลการปรับปรุงราว 1.5~1.6 เท่าในงานแยกวิเคราะห์จริง
  • เมื่อเทียบกับไลบรารี CSV คู่แข่ง (Sylvan, ReadLine, CsvHelper เป็นต้น) ก็พิสูจน์ได้ว่ามี throughput สูงกว่ามากและมีการจัดสรรทรัพยากรต่ำมาก

บทสรุปและสรุปย่อ

  • Sep 0.10.0 ก้าวข้ามข้อจำกัดของ ประสิทธิภาพการแยกวิเคราะห์ CSV ด้วยการผสาน การปรับแต่งซอฟต์แวร์ เข้ากับ ความสามารถของฮาร์ดแวร์รุ่นใหม่ (AVX-512, clock สูง)
  • หัวใจของนวัตกรรมคือการออกแบบ อัลกอริทึม SIMD รุ่นใหม่, การปรับปรุงโค้ด .NET JIT และโครงสร้างแอสเซมบลี
  • ผลของ การปรับปรุงประสิทธิภาพแบบสะสมและการเปลี่ยนรุ่นสถาปัตยกรรม ในช่วงเวลาสั้น ๆ นั้นน่าประทับใจ
  • Sep นำเสนอ ประสิทธิภาพสูง รองรับหลายแพลตฟอร์ม และขยายต่อได้จริง ในงานแยกวิเคราะห์ CSV ในระดับชั้นนำของอุตสาหกรรม

1 ความคิดเห็น

 
GN⁺ 2025-05-11
ความเห็นจาก Hacker News
  • ทั้งที่ Intel ลงทุนกับพื้นที่บนชิปอยู่หลายปีเพื่อรองรับ AVX-512 ในสินค้าผู้บริโภค แต่พอถึงจุดที่ไลบรารีต่าง ๆ เริ่มใช้งานกันมากขึ้น กลับถอด AVX-512 ออกจาก SKU ฝั่งผู้บริโภคเสียเอง เป็นเรื่องที่ชวนงงมาก ไม่ใช่ว่า AMD รองรับ AVX-512 ได้ดีกว่าอะไรนัก แค่ Intel ทิ้งสิ่งที่ตัวเองลงทุนไว้ ทำให้เกิดความย้อนแย้งที่ CPU ผู้บริโภคของ AMD กลับมี AVX-512 แทน
    • Intel มีแพตเทิร์นเดิม ๆ คือสร้างตลาดเทคโนโลยีขึ้นมา (Optane) แล้วก็ถอนตัวแบบกะทันหัน (Depth Cameras) จนผู้บริโภคสับสน ทุ่มสุดตัวกับเทคโนโลยีใหม่ แล้วถ้า uptake ไม่มากก็เลิกทันที Optane ก็ถูกยกเลิกตอนที่การรองรับใน Linux kernel กำลังจะสุกงอมพอดี แถมยังมีแนวทางลดต้นทุนแปลก ๆ อีกด้วย ถ้าย้อนไปในประวัติศาสตร์ก็ทำพลาดลักษณะนี้ซ้ำมาตั้งแต่ Intel iAPX 432
    • ในบทความนี้วัดได้ว่า บน CPU ของ AMD ความเร็วเป็นดังนี้: แบบต้นฉบับ 18GB/s, AVX2: 20GB/s, AVX512: 21GB/s ซึ่งแปลว่า AVX-512 แทบไม่มีข้อได้เปรียบเหนือ AVX2 เลย CPU ผู้บริโภคของ Intel ก็รองรับ AVX2 แม้แต่บน E-core การทดสอบนี้เป็นแบบ single-thread Intel ตัด AVX-512 ออกจากชิปเพื่อเพิ่มจำนวนคอร์ ผลคือรุ่นท็อปมี 24 คอร์ ขณะที่ AMD มี 16 คอร์ และเมื่อความต่างจาก AVX2 → AVX512 มีน้อยมาก ฝั่ง multi-thread คอร์ที่มากกว่าน่าจะชนะได้ สำหรับงานจริงส่วนใหญ่ จำนวนคอร์ที่เพิ่มขึ้นมีประโยชน์กว่า AVX-512 มาก ดังนั้นยกเว้นงานเฉพาะทางมาก ๆ ก็ถือว่าการตัดสินใจของ Intel สมเหตุสมผล
    • AVX-10 กำลังจะมาเร็ว ๆ นี้ และน่าจะมีความสามารถเกือบเหมือน AVX-512 (แต่ผมก็ไม่รู้เหมือนกันว่าต่างกันตรงไหน)
    • จุดที่น่าสนใจที่สุดในบทความนี้สำหรับผมคือ บน AMD 9950X ตัว parser ที่ใช้ AVX2 เร็วกว่าตัวที่อิง AVX-512 อยู่ราว 10% (20GB/s เทียบกับ 21GB/s) สุดท้ายแล้ว AVX-512 ดูเหมือนจะไม่ได้ช่วยผู้บริโภคจริง ๆ มากนัก แค่ parse CSV ได้ 20GB/s ก็ไม่มีอะไรให้บ่นแล้ว มีแต่พวกคลั่งแอสเซมบลีเท่านั้นที่สนใจเรื่องการรองรับนี้มาก
    • เรื่องที่ Intel ทำตัวแย่ ๆ แบบนี้ชวนอึ้งจริง ๆ
    • ถ้าจะให้สบายใจขึ้น Sep จะใช้ AVX-512 อัตโนมัติเมื่อทำได้โดยไม่ต้องตั้งค่าเพิ่ม มันทำงานได้ดีใน JIT runtime ดังนั้นแม้จะเผลอ target baseline ประสิทธิภาพต่ำสุดก็ไม่ได้เสียหายอะไร
    • Intel ยังซัพพอร์ตฝั่งซอฟต์แวร์ไม่ดีนัก iGPU ในโน้ตบุ๊กของผมก็ถือว่าดีพอสมควร แต่กลับไม่รองรับดีใน PyTorch เป็นต้น น่าเสียดาย ตรงกันข้าม llama.cpp และการอนุมานผ่าน Vulkan ทำได้ดี เลยหวังว่าซอฟต์แวร์ตัวอื่นจะรองรับแบบนี้บ้าง
  • แทนที่จะเปรียบเทียบ 4 ครั้งต่ออักขระ ('\n', '\r', ';', '“') แล้วทำ or 3 ครั้ง ยังมีทริกที่พบบ่อยซึ่งใช้ shuffle 1 ครั้ง, compare 1 ครั้ง และ or 0 ครั้งได้ ผมเคยเขียนบล็อกแนะนำทริกนี้ไว้ ลองดูได้ อีกอย่าง ในบทความนี้เองก็ใช้คำสั่ง vpternlogd และ vpor เพื่อลดจำนวนการทำ or
  • การที่ Intel ตัดสินใจว่าต้องใส่คอร์ช้าลงใน CPU ผู้บริโภค และถอด AVX-512 ออกหมดโดยไม่คิดเรื่อง “multi-pumping” เลย เป็นภาพที่ดูแย่มาก
    • สาเหตุหลักของตัวเลือกนี้คือปัญหากระบวนการผลิต 10nm yield แย่และต้นทุนสูงเกินไป เลยพยายามหั่นชิปให้มากที่สุดเพื่อทำกำไรด้วยคอร์ตระกูล Atom/การตลาดด้านพลังงานต่ำ ส่วนสินค้าราคาแพงก็เพิ่มขนาดและลด margin เพื่อรักษาตลาดเซิร์ฟเวอร์/คลาวด์ไว้ ท้ายที่สุดกำไรก็ลดลงและส่วนแบ่งตลาดก็หายไป แต่ก็ถือว่าอย่างน้อยพยายามแล้ว
  • คำกล่าวอ้างว่า Sep เร็วขึ้นเกือบ 3 เท่าในเวลา 2 ปีหลังเปิดตัว (มิถุนายน 2023) ยังเป็นประเด็นถกเถียงถ้าคำนึงถึงการกระโดดของฮาร์ดแวร์ด้วย
    • ถ้าดูบนฮาร์ดแวร์เดียวกัน การพัฒนาของซอฟต์แวร์ (0.9.0 เทียบกับ 0.10.0) คือ 17% และถ้าเอา 13088 ของ 0.9.0 บวกอีก 17% จะได้ 15375 เมื่อเทียบกับ 7335 ของ 0.1.0 ก็เท่ากับดีขึ้นราว 2.1 เท่า
    • มีการอ้างว่าบนฮาร์ดแวร์เดียวกัน sep ดีขึ้นจากเวอร์ชันก่อนราว 3GB/s และก็ระบุความเร็วจริงกับข้อมูลฮาร์ดแวร์ไว้อย่างโปร่งใส
    • ยุคนี้ไม่ใช่ยุคกฎของมัวร์แบบเดิมแล้ว จึงยากที่จะหวังความเร็วเพิ่ม 3 เท่าจากฮาร์ดแวร์อย่างเดียว ระดับนี้ก็ถือว่าน่าประทับใจพอแล้วในยุคปัจจุบัน
    • แน่นอนว่าการอ้างว่าเร็วขึ้น 3 เท่าโดยไม่คำนึงถึงการกระโดดของฮาร์ดแวร์นั้นมีช่องให้โต้แย้ง แต่ก็ยังน่าสนใจในฐานะอีกมุมหนึ่งของการมองประสิทธิภาพซอฟต์แวร์จริงในแต่ละปี
    • กราฟ benchmark ข้ามรุ่น CPU ไปตั้ง 4 รุ่นแล้วค่อยแสดงเหมือนเป็น “การเพิ่มประสิทธิภาพครั้งใหญ่” เลยทำให้ไม่น่าเชื่อถือ
  • คาดหวังว่า Arthur Whitney จะถูกผลลัพธ์นี้กระตุ้นจนเขียนโค้ด 1 บรรทัดที่แซงมันได้ หรือทำลายสถิติด้วย 1 บรรทัดพร้อมอัปเดต shakti engine หรือข่าวอัปเดตอื่น ๆ อยากเห็นความก้าวหน้าต่อไป
  • แค่นึกว่ามีใครต้องประมวลผล CSV หลายสิบล้านบรรทัดด้วยความเร็วระดับนี้ก็สยองแล้ว
    • ผมเองก็เคยเจอแบบนั้น ตอนแรกเลือก CSV เพราะข้อมูลยังน้อย และคนที่ไม่ใช่นักพัฒนา โดยเฉพาะคนที่ใช้ Excel เก่ง อ่านได้ง่าย จึงใช้ CSV กับ log/กระบวนการต่าง ๆ ได้อย่างเรียบร้อย แต่พอข้อมูลโตขึ้น 10 เท่า 100 เท่า การ optimize เพื่อ ingest CSV ระดับหลายร้อยล้านบรรทัดให้เร็วขึ้นก็กลายเป็นความจำเป็นจริง ๆ การปรับแต่งแบบนี้เหมือนช่วยซื้อเวลาเพื่อค่อย ๆ ย้ายกระบวนการภายในไปสู่ฟอร์แมตที่เหมาะกว่า
    • CSV เป็นฟอร์แมตที่ใช้กันภายในองค์กรบ่อยกว่าที่คิด และยังบีบอัดได้ง่าย (deflate) ด้วย ผมเคยทำงานกับโค้ดที่ปล่อยข้อมูล Netflow เป็น CSV ด้วยความเร็วระดับการ์ด NIC มาก่อน ส่วนตัวคิดว่าถ้าจะซับซ้อนขนาดนั้นใช้ protocol buffers ไปเลยน่าจะดีกว่า protobuf ก็ไม่ได้เป็นฟอร์แมตที่ยากอะไรนัก แต่กลับไม่ค่อยมีการนำมาใช้
    • สิ่งที่น่ากลัวยิ่งกว่าคือการต้องเก็บผลลัพธ์จากการประมวลผล CSV ระดับ 21GB/s ไว้ที่ไหนสักแห่ง ต่อให้เป็นการสรุปผลที่มีประโยชน์ แต่ด้วยความเร็วระดับนี้สุดท้ายมันก็ต้องถูกเก็บสะสมไว้ที่ไหนแน่ ๆ เลยอดสงสัยไม่ได้
    • แม้จะมีข้อเสียมากมาย แต่ผมก็ยังคิดว่า CSV ยังเป็นฟอร์แมตแลกเปลี่ยนข้อมูลที่พบได้บ่อยที่สุดอยู่ดี
    • ในวงการการเงิน ทุกคนแชร์ CSV กันได้ และเพราะเป็นข้อความล้วนจึงโยนเข้าไปที่ไหนก็ประมวลผลได้
    • ตัวอย่างหนึ่งคือไฟล์ cartesian product ที่ฝ่ายบัญชีส่งมาให้ตอนสิ้นปี
    • ผมเองก็อยู่ในสภาพที่ต้องลำบากกับ CSV แบบโบราณพวกนี้จริง ๆ
    • เกือบทุกกรณี HDF5 ดีกว่า CSV แต่ถึงอย่างนั้นก็อธิบายได้ยากนอกจากความไม่รู้ ความขี้เกียจ หรือเหตุผลว่า “มันก็ใช้ได้อยู่แล้ว”
  • ผมเข้ามาเพราะคาดว่าจะได้เห็นโค้ดแอสเซมบลี แต่กลับเป็น C# ซึ่งทั้งน่าประหลาดใจและน่าประทับใจ เป็นผลงานที่ยอดเยี่ยม
    • .NET รุ่นใหม่คือ “ภาษาระดับสูง” ที่ผสาน SIMD และ vector intrinsics ได้ลึกที่สุด Microsoft โดย Tanner Gooding เป็นคนผลักดันความก้าวหน้าจำนวนมาก และบทความบล็อกที่เกี่ยวข้องก็ดีมาก
  • สิ่งที่ทำให้งงคือในเนื้อหาไม่ได้อธิบายให้ชัดว่าโค้ด 21GB/s นี้ทำอะไรกันแน่ เช่น ฟอร์แมตที่ parse มีรายละเอียดอะไรบ้าง (เช่น การจัดการเครื่องหมายคำพูดใน CSV) และหลัง parse แล้วเอาผลไปใช้อย่างไร (เช่น ใส่ลงในโครงสร้างข้อมูลหรือไม่) ก็ไม่ชัดเจน
    • จากค่าที่คำนวณในบทความ ns/row อยู่ราว 27ns/row (ประมาณ 37,000 row ต่อวินาที) แต่ถ้าได้ 21GB/s จริง จะเท่ากับราว 570KB ต่อ row ซึ่งดูเป็น benchmark ที่ผิดธรรมชาติมาก
  • จากประสบการณ์ของผม การเขียนโค้ด SIMD แบบ custom มักได้ประโยชน์เหนือ auto-vectorization ของคอมไพเลอร์ยุคใหม่ไม่มากนัก (โดยเฉพาะในโค้ดที่เป็นมิตรกับเวกเตอร์อยู่แล้ว) แต่กรณีพิเศษอย่างการ parse JSON อาจต่างออกไป
  • ช่วงหลังผมต้องทำงานกับไฟล์ dump CSV ขนาด 300GB และเสียเวลากับการจัดการกับการตรวจสอบความสมบูรณ์มาก จึงต้องการ CSV parser ที่เร็วแบบนี้อย่างมาก
    • ผมไม่เข้าใจเลยว่าทำไมถึงไม่ใช้ฟอร์แมตที่เหมาะกับการเก็บข้อมูล floating-point โดยเฉพาะ HDF5 เป็นทางเลือกที่ดีกว่ามาก