Đạt 21 GB/s khi phân tích CSV bằng SIMD trên AMD 9950X
(nietras.com)- Sep 0.10.0 đạt 21 GB/s trong phân tích CSV mức thấp nhờ tối ưu cho CPU hỗ trợ AVX-512 như AMD 9950X (Zen 5), nhanh hơn mức khoảng 18 GB/s trước đó
- Mức tăng hiệu năng đến từ việc thay đổi cấu trúc parser để giảm nút thắt do vòng đi-về của thanh ghi mask trong quá trình tạo mã AVX-512 của .NET 9.0
- Parser AVX-512-to-256 mới tải
charbằng 512 bit rồi chuyển thành vector byte 256 bit, tránh được chi phí xử lý mask và hoán vị riêng biệt - Hiệu năng phân tích mức thấp của Sep đã được cải thiện gần 3 lần, từ khoảng 7 GB/s trên 5950X/.NET 7.0 của bản 0.1.0 năm 2023 lên khoảng 21 GB/s trên 9950X/.NET 9.0 của bản 0.10.0
- Ở benchmark cấp cao hơn, Sep đa luồng trên 9950X xử lý 1 triệu dòng package assets trong 72.213ms, tương đương khoảng 8.0 GB/s; với dữ liệu floats cũng đạt khoảng 8.1 GB/s
Mục tiêu và kết quả của Sep 0.10.0
- Sep 0.10.0 được phát hành ngày 22/4/2025, bao gồm tối ưu cho CPU hỗ trợ AVX-512 như AMD 9950X (Zen 5) và benchmark trên 9950X
- Theo chuẩn phân tích CSV mức thấp, Sep đạt 21 GB/s trên 9950X
- Trước 0.10.0, trên cùng 9950X con số này vào khoảng 18 GB/s
- Phạm vi phân tích là việc parse
Rowsmức thấp của dữ liệu CSV package assets, và mọi số liệu đều dựa trên một luồng đơn - Số liệu benchmark có thể dao động vài điểm phần trăm, nên ở một số bản phát hành cụ thể có thể thấy hồi quy nhỏ
Thay đổi hiệu năng từ 0.1.0 đến 0.10.0
- Hiệu năng của Sep được cải thiện dần theo thời gian nhờ tác động kết hợp của thay đổi mã nguồn, phiên bản .NET và thế hệ CPU
- Diễn biến hiệu năng tiêu biểu như sau
- 0.1.0, 5950X, .NET 7.0: khoảng 7 GB/s
- 0.3.0, 5950X, .NET 8.0: khoảng 12 GB/s
- 0.6.0, 5950X, .NET 9.0: khoảng 13 GB/s
- 0.9.0, 9950X, .NET 9.0: khoảng 18 GB/s
- 0.10.0, 9950X, .NET 9.0: khoảng 21 GB/s
- Trong chưa đầy 2 năm kể từ khi Sep được công bố vào tháng 6/2023, tốc độ đã tăng gần 3 lần
- Nếu so sánh Sep 0.9.0 trên 5950X với Sep 0.10.0 trên 9950X, mức cải thiện là khoảng 1.6 lần
- Xung boost của 9950X là 5.7GHz, còn 5950X là 4.9GHz
- Chỉ riêng chênh lệch xung nhịp này có thể giải thích khoảng 1.2 lần
Nút thắt thanh ghi mask trong quá trình tạo mã AVX-512 của .NET
- Sep đã hỗ trợ AVX-512 từ bản 0.2.3, nhưng ở thời điểm đó .NET 8 chưa hỗ trợ tường minh các thanh ghi mask
k1-k8của AVX-512 - Trong cách tạo mã AVX-512 cũ, kết quả so sánh đi vào thanh ghi mask rồi được chuyển sang thanh ghi thường, sau đó lại quay về thanh ghi mask
- Sau khi nâng cấp lên 9950X, Sep 0.9.0 ghi nhận khoảng 18 GB/s trong phân tích CSV mức thấp, nhanh hơn khoảng 1.4 lần so với 5950X
- Khi đổi parser bằng biến môi trường để so sánh, parser AVX2 đạt khoảng 20 GB/s trên 9950X, nhanh hơn khoảng 10% so với parser AVX-512 cũ
- Từ khác biệt này có thể xác nhận việc xử lý thanh ghi mask của AVX-512 vẫn đang ảnh hưởng đến hiệu năng
Cấu trúc cơ bản của vòng lặp parse trong Sep
- Mọi parser của Sep đều theo cùng một cấu trúc cơ bản và hỗ trợ hai nhánh xử lý khác nhau về dấu ngoặc kép thông qua một phương thức generic
ParseParseColInfos: dùng khi có xử lý dấu ngoặc kép và cần theo dõi nhiều trạng thái hơnParseColEnds: dùng khi không có xử lý dấu ngoặc kép
- Quá trình parse được thực hiện theo từng span
charlấy từ mảng, ví dụ kích thước là 16K- Kích thước này đủ nhỏ để nằm trong cache CPU và cũng có lợi cho đa luồng hiệu quả về sau
- Vòng lặp sẽ tải dữ liệu ký tự 16 bit vào thanh ghi SIMD, chuyển chúng thành thanh ghi SIMD byte rồi so sánh với các ký tự đặc biệt của CSV
- Các ký tự được so sánh gồm
\n,\r,",;v.v.
- Các ký tự được so sánh gồm
- Kết quả so sánh được chuyển thành bitmask, sau đó chỉ các bit đã được đặt trong mask mới được parse tuần tự
- Chênh lệch hiệu năng phụ thuộc rất lớn vào việc mã SIMD C# này được JIT trong .NET biên dịch thành mã máy như thế nào
Parser AVX-512 cũ và điều chỉnh trong 0.10.0
SepParserAvx512PackCmpOrMoveMaskTzcntcủa 0.9.0 tải mỗi lần 32charvào hai thanh ghi SIMD 512 bit, rồi đóng gói chúng thành một vector byte 512 bit để xử lý 64 ký tự mỗi vòng lặp- Dữ liệu sau khi đóng gói bị xáo trộn thứ tự nên phải sắp xếp lại bằng
PermuteVar8x64 - Trong assembly của .NET 9.0, mỗi
Vec.Equalssẽ tạo thành hai lệnhvpcmpeqbvàvpmovm2b, khiến việc di chuyển qua lại giữa thanh ghi mask nhưk1và thanh ghi vector thườngzmmlặp đi lặp lại - Trong Sep 0.10.0, lời gọi
MoveMaskđược đưa lên sớm hơn để giảm số lần đi-về giữa thanh ghi mask và thanh ghi thường- Ở các parser khác,
MoveMaskchỉ được gọi khi cần để giảm số lệnh ở nhánh nhanh “không có ký tự đặc biệt”
- Ở các parser khác,
- Sau điều chỉnh, việc chuyển từ thanh ghi mask sang thanh ghi thường vẫn còn, nhưng tổng số lệnh assembly đã giảm xuống
AVX2 và parser AVX-512-to-256 mới
- Assembly của
SepParserAvx2PackCmpOrMoveMaskTzcntdựa trên AVX2 không có thanh ghi mask, nên có cấu trúc thẳng hơn - Nhờ cấu trúc này, parser AVX2 nhanh hơn parser AVX-512 cũ của 0.9.0
SepParserAvx512To256CmpOrMoveMaskTzcntmới trong 0.10.0 tảicharbằng lệnh AVX-512 rồi tạo vector byte 256 bit bằngConvertToVector256ByteWithSaturation- Lệnh thực tế là
vpmovuswb - Mỗi vòng lặp chỉ xử lý 32
char, nhưng cấu trúc đơn giản hơn
- Lệnh thực tế là
- Cách làm này tránh được vấn đề với thanh ghi mask 512 bit, và dữ liệu đã đóng gói cũng nằm đúng thứ tự trong
ymm4, nên không cần hoán vị riêng nữa - Parser mới đã nâng hiệu năng parse của Sep trên 9950X lên khoảng 21 GB/s
Benchmark mức thấp trên 9950X theo từng parser
- Kết quả chạy tất cả parser bằng biến môi trường trên AMD 9950X cho thấy parser AVX-512-to-256 mới là nhanh nhất
- Các kết quả chính như sau
SepParserAvx512To256CmpOrMoveMaskTzcnt: 21597.7 MB/s, 27.0 ns/row, 1.351msSepParserVector256NrwCmpExtMsbTzcnt: 20608.5 MB/s, 28.3 ns/row, 1.416msSepParserAvx2PackCmpOrMoveMaskTzcnt: 20599.3 MB/s, 28.3 ns/row, 1.417msSepParserAvx512PackCmpOrMoveMaskTzcnt: 19944.3 MB/s, 29.3 ns/row, 1.463ms
- Parser đa nền tảng dựa trên
Vector256đã đạt gần ngang AVX2 - Các parser đa nền tảng dựa trên
Vector128vàVector512vẫn nhanh, nhưng chậm hơn 5~10%, vàVector512còn chậm hơnVector128 SepParserIndexOfAnybị bỏ xa với 2787.0 MB/s, cònVector64không được tăng tốc trên 9950X nên chỉ dừng ở 459.9 MB/s
Benchmark cấp cao hơn trên 5950X và 9950X
- Với dữ liệu package assets, kết quả xử lý 1 triệu dòng cho thấy 9950X nhanh hơn 5950X đáng kể
- 5950X
Sep_MT: 119.430ms, 4888.1 MB/s - 9950X
Sep_MT: 72.213ms, 8084.1 MB/s
- 5950X
- Trên 9950X,
Sepđơn luồng xử lý 1 triệu dòng package assets trong 291.979ms, tương đương 1999.4 MB/s - Trên cùng benchmark package assets của 9950X, các đối tượng so sánh đạt các mức sau
Sylvan: 413.265ms, 1412.6 MB/sReadLine_: 377.033ms, 1548.4 MB/s, lượng cấp phát 1991.04MBCsvHelper: 1005.323ms, 580.7 MB/s
- Với dữ liệu floats, Sep đa luồng trên 9950X cũng xử lý 25.000 dòng trong 2.497ms, tương đương 8136.8 MB/s
- Mức cải thiện ở benchmark cấp cao hơn khi chuyển từ 5950X sang 9950X cũng tương tự benchmark mức thấp, khoảng 1.5~1.6 lần
Chưa có bình luận nào.