1 điểm bởi GN⁺ 2025-05-11 | 1 bình luận | Chia sẻ qua WhatsApp
  • Phiên bản mới nhất Sep 0.10.0 đạt tốc độ phân tích cú pháp CSV ấn tượng 21 GB/s trên AMD 9950X
  • Hiệu năng được cải thiện đáng kể nhờ hỗ trợ AVX-512 và khắc phục vấn đề thanh ghi mask
  • Trình phân tích AVX-512-to-256 mới cho kết quả vượt qua AVX2 và trình phân tích AVX-512 trước đây
  • Trong môi trường đa luồng, xử lý 1 triệu dòng trong 72ms và ghi nhận băng thông 8 GB/s
  • Nhờ tối ưu hóa phần mềm và phần cứng liên tục, hiệu năng đã cải thiện gần 3 lần sau 2 năm

Tổng quan về bản phát hành Sep 0.10.0 và cải thiện hiệu năng

  • Trong bản phát hành 0.10.0 gần đây, Sep đã tối ưu cho các CPU hỗ trợ AVX-512 (ví dụ: AMD 9950X, Zen 5) và cập nhật kết quả benchmark
  • Ở phiên bản mới nhất, công cụ ghi nhận thành tích ấn tượng 21 GB/s trong phân tích cú pháp CSV cấp thấp
  • So với phiên bản trước, đây là mức tăng đáng kể từ 18 GB/s
  • Có thể xem chi tiết các thay đổi trong GitHub release và README của Sep
  • Bài viết giải thích quá trình cải thiện hiệu năng bằng mã C# dựa trên SIMD và assembly SIMD x64 để vượt qua sự kém hiệu quả của mã máy AVX-512 trong .NET 9.0

Quá trình phát triển hiệu năng của Sep

  • Sự tiến hóa của Sep từ 0.1.0 ban đầu đến 0.10.0, từ .NET 7.0 lên 9.0, và từ AMD 5950X (Zen 3) sang 9950X (Zen 5) được thể hiện trực quan
  • Benchmark được đo theo đơn luồng và có thể có dao động nhỏ giữa các bản phát hành
  • Thông điệp cốt lõi là cả những lần refactor lớn (viết lại cấu trúc nội bộ ở 0.2.0) lẫn các thay đổi nhỏ tích lũy đều liên tục thúc đẩy cải thiện hiệu năng
  • Nhờ sự phát triển song hành của phần cứng và phần mềm, tốc độ phân tích đạt 21 GB/s, tương đương mức cải thiện gần 3 lần trong khoảng 2 năm
  • Chỉ riêng thay đổi giữa các thế hệ phần cứng (5950X→9950X, 4.9→5.7GHz) cũng đã là cơ sở cho mức cải thiện hơn 1.2x

Sinh mã AVX-512 và vấn đề thanh ghi mask

  • Sep đã hỗ trợ AVX-512 từ phiên bản 0.2.3, nhưng gặp giới hạn trong việc tận dụng các thanh ghi mask (k1-k8) của AVX-512
  • Trong .NET 8, chưa có hỗ trợ trực tiếp cho thanh ghi mask, nên việc sao chép và chuyển đổi lặp đi lặp lại giữa các thanh ghi thông thường làm giảm hiệu năng
  • Ở giai đoạn đầu khi chưa có CPU hỗ trợ AVX-512 riêng, tác giả chỉ có thể thử nghiệm hạn chế trên Xeon Silver 4316 và xác nhận nó là nhanh nhất

Nâng cấp lên 9950X và so sánh AVX-512 với AVX2

  • Gần đây, sau khi nâng cấp CPU từ Zen 3 (5950X) lên Zen 5 (9950X), benchmark của Sep đã đạt 18 GB/s
  • Khi so sánh trực tiếp trình phân tích AVX-512 và AVX2, kết quả hơi bất ngờ là AVX2 đạt khoảng 20 GB/s, nhanh hơn AVX-512 khoảng 10%
  • Điều này cho thấy sự kém hiệu quả trong xử lý thanh ghi mask của JIT .NET vẫn là một vấn đề

Mã parser, phân tích assembly và trình phân tích AVX-512-to-256 mới

  • Tất cả parser của Sep đều xử lý char span theo khối 16K và sử dụng các phép so sánh dựa trên thanh ghi SIMD (như Vector256)
  • SIMD được dùng để nhanh chóng xác định các ký tự đặc biệt (xuống dòng, dấu ngoặc kép, dấu phân cách, v.v.) rồi chuyển thành bitmask để tối ưu các phép toán tập hợp
  • Parser dựa trên AVX-512 có nhiều phép toán dư thừa do phải di chuyển lặp lại giữa thanh ghi mask (k1, v.v.) và thanh ghi thông thường (zmm, v.v.)
  • Trong 0.10.0, lời gọi MoveMask được đưa lên sớm hơn để giảm tối đa việc chuyển đổi mask không cần thiết, từ đó giảm số lượng lệnh assembly
  • Parser AVX2 không có thanh ghi mask nên cấu trúc đơn giản hơn nhiều, và trên thực tế nhanh hơn AVX-512
  • Trình phân tích AVX-512-to-256 mới đọc dữ liệu bằng AVX-512 rồi dùng lệnh chuyển đổi 256-bit để né hẳn vấn đề xử lý mask, giúp cách triển khai gọn hơn và đạt hiệu năng trên 21 GB/s

Tổng hợp benchmark của nhiều loại parser

  • Khi dùng biến môi trường để so sánh benchmark của mọi loại parser, parser AVX-512-to-256 là nhanh nhất với 21.5 GB/s
  • Các parser dựa trên AVX2 và Vector256 cũng cho hiệu năng rất sát, chỉ chênh trong khoảng 5%
  • Các parser Vector128 và Vector512 chậm hơn AVX2 từ 5~10%; đặc biệt parser Vector512 còn chậm hơn cả Vector128
  • Parser IndexOfAny chậm hơn rõ rệt so với các parser SIMD khác. Vector64 không được tăng tốc trên 9950X nên hiệu năng rất thấp
  • Các parser SIMD dựa trên AVX-512 và AVX2 chứng minh hiệu năng áp đảo so với các parser CSV cùng loại

Benchmark cấp cao hơn: so sánh 5950X và 9950X

  • Với dữ liệu 1 triệu dòng tài sản package, Sep_MT ghi nhận 72ms (8GB/s) trên 9950X và 119ms (4.9GB/s) trên 5950X
  • Ngay cả với dữ liệu tải thực tế (như float), hệ thống vẫn đạt băng thông ~8GB/s trên 9950X trong chế độ đa luồng
  • Sự thay đổi thế hệ (5950X→9950X) mang lại mức cải thiện khoảng 1.5~1.6 lần trong các tác vụ phân tích cú pháp ứng dụng thực tế
  • So với các thư viện CSV cạnh tranh (Sylvan, ReadLine, CsvHelper, v.v.), Sep cho thấy thông lượng vượt trội và mức cấp phát tài nguyên tối thiểu

Kết luận và tóm tắt

  • Sep 0.10.0 vượt qua giới hạn hiệu năng phân tích cú pháp CSV nhờ sự kết hợp giữa tối ưu hóa phần mềmtính năng phần cứng hiện đại (AVX-512, xung nhịp cao)
  • Thiết kế thuật toán SIMD hiện đại cùng các cải tiến trong mã JIT .NET và cấu trúc assembly là trọng tâm của bước tiến này
  • Hiệu quả của cải thiện hiệu năng tích lũy trong thời gian ngắn cùng với thay đổi thế hệ kiến trúc là rất ấn tượng
  • Sep đang cho thấy một chuẩn mực thực tế hàng đầu ngành về hiệu năng cao, đa nền tảng và khả năng mở rộng trong lĩnh vực phân tích cú pháp CSV

1 bình luận

 
GN⁺ 2025-05-11
Ý kiến Hacker News
  • Việc Intel đầu tư suốt nhiều năm, thậm chí tốn cả diện tích die, để hỗ trợ AVX-512 trên sản phẩm tiêu dùng, nhưng rồi đúng lúc các thư viện bắt đầu dùng nó ngày càng nhiều thì lại gỡ AVX-512 khỏi SKU tiêu dùng là điều cực kỳ khó hiểu; không phải AMD hỗ trợ AVX-512 tốt hơn gì, mà chỉ là Intel tự từ bỏ thứ họ đã đầu tư nên mới dẫn đến dòng chảy mỉa mai là CPU tiêu dùng của AMD lại có AVX-512
    • Intel luôn lặp lại mô thức xây dựng thị trường công nghệ (Optane) rồi rút lui đột ngột (Depth Cameras) khiến người dùng hoang mang; họ dồn toàn lực vào công nghệ mới rồi nếu không thấy uptake là bỏ ngay, Optane bị dừng đúng lúc hỗ trợ trong Linux kernel sắp trưởng thành, còn có cả các chiến lược cắt giảm chi phí kỳ quặc; nhìn ngược về lịch sử thì từ thời Intel iAPX 432 họ đã lặp lại cùng một sai lầm
    • Bài này cho thấy trên CPU AMD: bản gốc 18GB/s, AVX2: 20GB/s, AVX512: 21GB/s, tức AVX-512 gần như không có lợi thế so với AVX2; CPU tiêu dùng Intel hỗ trợ AVX2 cả trên E-core, benchmark là đơn luồng, Intel bỏ AVX-512 khỏi chip để lấy chỗ cho nhiều core hơn nên bản cao cấp nhất là 24 core, còn AMD là 16 core; do chênh lệch AVX2→AVX512 rất nhỏ nên ở đa luồng bên nhiều core hơn có thể thắng; với phần lớn workload thực tế, tăng số core có ích hơn AVX-512, nên trừ một vài tác vụ rất đặc thù thì quyết định của Intel là hợp lý
    • Sắp tới AVX-10 sẽ xuất hiện và dự kiến có bộ tính năng gần như giống AVX-512 (tôi cũng không rõ khác nhau ở đâu)
    • Điều thú vị nhất trong bài này là trên AMD 9950X, parser AVX2 nhanh hơn parser dựa trên AVX-512 khoảng 10% (20GB/s so với 21GB/s), rốt cuộc có vẻ AVX-512 không giúp ích nhiều cho người dùng phổ thông; CSV parsing đạt 20GB/s là tôi đã không có gì phàn nàn, chỉ những người mê assembly mới nhạy cảm với chuyện có hỗ trợ hay không
    • Thật khó tin là Intel lại hành xử ngu ngốc như vậy
    • Nếu cần chút an ủi thì Sep sẽ dùng AVX-512 bất cứ khi nào có thể mà không cần cấu hình riêng; nó chạy tốt trong JIT runtime nên vô tình cũng không bị thiệt khi nhắm tới mức nền hiệu năng thấp nhất
    • Intel cũng hỗ trợ phần mềm không mấy tốt; iGPU trên laptop của tôi cũng khá ổn nhưng lại không được hỗ trợ tử tế trong PyTorch và các phần mềm tương tự, khá đáng tiếc; llama.cpp và suy luận bằng Vulkan thì chạy tốt, nên tôi mong các phần mềm khác cũng hỗ trợ theo kiểu đó
  • Thay vì làm bốn lần so sánh cho mỗi ký tự ('\n', '\r', ';', '“') rồi ba phép or, có thể dùng một mẹo phổ biến để chỉ cần 1 lần shuffle, 1 lần so sánh và 0 phép or; tôi đã viết một bài blog giới thiệu mẹo này nên có thể tham khảo; mà ngay trong bài này họ cũng đã giảm số phép or bằng vpternlogdvpor
  • Việc Intel quyết định phải nhét các core chậm vào CPU tiêu dùng rồi xóa sạch AVX-512 mà chẳng cân nhắc cả “multi-pumping” thật rất chướng mắt
    • Nguyên nhân chính của lựa chọn này là vấn đề tiến trình 10nm; yield kém và chi phí quá đắt nên họ cố cắt chip tối đa để kiếm lợi nhuận bằng các dòng core kiểu Atom/marketing điện năng thấp, còn sản phẩm cao cấp thì tăng kích thước và giảm biên lợi nhuận để giữ thị trường server/cloud; rốt cuộc lợi nhuận vẫn giảm và cũng mất thị phần, nhưng ít nhất họ đã cố thử
  • Tuyên bố đạt mức tăng tốc khoảng 3 lần chỉ trong 2 năm kể từ khi Sep ra mắt (tháng 6/2023) vẫn gây tranh cãi nếu tính cả bước nhảy phần cứng
    • Trên cùng phần cứng, mức cải thiện phần mềm (0.9.0 so với 0.10.0) là 17%, và nếu cộng thêm 17% vào 13088 của 0.9.0 thì ra 15375, so với 7335 của 0.1.0 là tăng khoảng 2,1 lần
    • Họ khẳng định sep cải thiện 3GB/s so với phiên bản trước trên cùng phần cứng, và tốc độ thực tế cùng thông tin phần cứng cũng được ghi khá minh bạch
    • Bây giờ không còn là thời đại kiểu định luật Moore nữa nên khó kỳ vọng chỉ nhờ phần cứng mà được tăng tốc 3x; dù vậy mức này theo tôi vẫn là thành tựu rất ấn tượng ở thời điểm hiện nay
    • Rõ ràng việc tuyên bố tăng 3 lần mà không tính bước nhảy phần cứng là điều có thể tranh luận, nhưng dù sao đây vẫn là một góc nhìn thú vị về hiệu năng phần mềm thực tế qua từng năm
    • Biểu đồ benchmark bỏ qua đến 4 thế hệ CPU rồi đột nhiên khiến mọi thứ trông như “cải thiện hiệu năng lớn”, nên không đáng tin lắm
  • Tôi mong Arthur Whitney sẽ bị kết quả này kích thích rồi vượt qua nó bằng 1 dòng code, hoặc cùng với bản cập nhật engine shakti lại phá kỷ lục chỉ bằng 1 dòng, hay có một bản tin cập nhật nào đó; hy vọng sẽ còn tiến bộ thêm
  • Chỉ nghĩ đến việc ai đó phải xử lý CSV hàng chục triệu dòng ở tốc độ này thôi đã thấy rùng mình
    • Tôi cũng từng gặp tình huống như vậy; ban đầu chọn CSV vì dữ liệu còn nhỏ, người không phải lập trình viên — đặc biệt là những người dùng Excel giỏi — dễ đọc, nên log/process cũng được xử lý gọn gàng bằng CSV; nhưng khi dữ liệu tăng lên gấp 10 hay 100 lần thì tối ưu để ingest CSV ở quy mô hàng tỷ dòng lại trở thành nhu cầu thực tế; các tối ưu kiểu này về cơ bản là để câu thêm thời gian chuyển dần các quy trình nội bộ sang định dạng phù hợp hơn
    • CSV thực ra là định dạng được dùng nội bộ phổ biến hơn người ta nghĩ, lại có lợi thế là dễ nén (deflate); trước đây tôi từng xử lý code đẩy dữ liệu Netflow dưới dạng CSV ở tốc độ bằng tốc độ card NIC; cá nhân tôi nghĩ nếu đã phải xử lý phức tạp như thế thì thà dùng protocol buffers còn hơn, protobuf cũng đâu phải định dạng quá khó mà vẫn ít được áp dụng
    • Điều khiến tôi sợ hơn là ý nghĩa của việc lưu lại kết quả sau khi xử lý CSV ở mức 21GB/s; dù tổng hợp có hữu ích đến đâu thì ở tốc độ này dữ liệu cuối cùng cũng phải chất đống ở đâu đó, khiến tôi rất tò mò
    • Dù có nhiều nhược điểm, tôi vẫn nghĩ CSV cho tới nay là định dạng trao đổi dữ liệu phổ biến nhất
    • Trong ngành tài chính, CSV là thứ ai cũng có thể chia sẻ, lại là văn bản thuần nên có thể quăng vào đâu cũng xử lý được
    • Có thể lấy ví dụ là file cartesian product mà bộ phận kế toán gửi vào cuối năm
    • Tôi đúng là đang khổ sở vì phải xử lý kiểu CSV cổ lỗ sĩ như vậy
    • Gần như trong mọi trường hợp HDF5 đều tốt hơn CSV, vậy mà ngoài sự thiếu hiểu biết, lười biếng, hoặc lý do “vì nó vẫn chạy được” ra thì thật khó giải thích
  • Tôi vào đây mong thấy mã assembly nhưng lại là C#, khá bất ngờ và ấn tượng; kết quả rất tuyệt
    • .NET hiện đại là “ngôn ngữ bậc cao” tích hợp SIMD và vector intrinsics sâu nhất; Tanner Gooding của Microsoft đã thúc đẩy rất nhiều tiến bộ, và các bài blog liên quan cũng rất hay
  • Điều gây khó hiểu là trong bài gốc không định nghĩa rõ đoạn code 21GB/s thực sự làm chính xác điều gì; ví dụ định dạng được parse cụ thể là gì (như xử lý dấu nháy trong CSV), và sau khi parse thì kết quả được dùng thế nào (có đưa vào cấu trúc dữ liệu hay không) đều không rõ
    • Theo cách tính ns/row trong bài thì ra khoảng 27ns/row (37.000 row mỗi giây), nhưng nếu là 21GB/s thì mỗi row sẽ cỡ 570KB, nên benchmark này có vẻ rất bất thường
  • Theo kinh nghiệm của tôi, rất khó thu được lợi ích lớn từ SIMD tùy biến so với auto-vectorization của compiler hiện đại (đặc biệt với code vốn đã thân thiện với vector), dù trong các trường hợp đặc biệt như JSON parsing thì có hơi khác
  • Gần đây tôi phải làm việc với một bản trích xuất CSV 300GB, và tốn quá nhiều thời gian cho thao tác cùng kiểm tra tính toàn vẹn, nên thực sự rất cần các parser CSV nhanh như thế này
    • Tôi không hiểu vì sao người ta không dùng định dạng chuyên để lưu dữ liệu dấu phẩy động; HDF5 là một lựa chọn thay thế tốt hơn rất nhiều