Bend - Ngôn ngữ bậc cao chạy trên GPU (dùng HVM2)
(github.com/HigherOrderCO)- Bend là một ngôn ngữ lập trình song song bậc cao, hướng tới việc kết hợp sức biểu đạt như Python·Haskell với khả năng thực thi song song quy mô lớn kiểu CUDA, và chạy trên runtime HVM2
- Hỗ trợ hàm bậc cao có closure, cấp phát đối tượng nhanh, đệ quy không giới hạn, continuation, nhưng vẫn chạy trên phần cứng song song như GPU mà không cần ký hiệu song song hóa tường minh như tạo thread, lock, mutex hay atomic
- Mục tiêu thiết kế hiện tại là khả năng mở rộng hiệu năng theo số lượng lõi, hỗ trợ hơn 10.000 thread đồng thời, nhưng phiên bản hiện tại có thể có hiệu năng đơn lõi thấp và việc cải thiện sinh mã·tối ưu hóa vẫn đang tiếp tục
- Cách chạy được chia thành
bend run-rs,bend run-c,bend run-cu; với mã có thể song song hóa, chỉ cần đổi lệnh chạy là có thể thực thi song song trên trình thông dịch C hoặc trình thông dịch CUDA - Hỗ trợ Windows vẫn đang được phát triển nên WSL2 là giải pháp thay thế, và hiện tại việc chạy trên GPU chỉ hỗ trợ NVIDIA GPU
Mô hình lập trình mà Bend hướng tới
- Bend là một ngôn ngữ lập trình chạy trên phần cứng song song quy mô lớn trong khi vẫn giữ được trải nghiệm của ngôn ngữ bậc cao
- Cung cấp các tính năng của những ngôn ngữ giàu sức biểu đạt như Python và Haskell
- Cấp phát đối tượng nhanh
- Hàm bậc cao có closure
- Đệ quy không giới hạn
- Continuation
- Chạy trên phần cứng song song quy mô lớn như GPU giống CUDA, với mục tiêu tăng tốc gần tuyến tính dựa trên số lượng lõi
- Không cần phải tự viết các phần sau để chạy song song
- Tạo thread
- Lock
- Mutex
- Atomic
- Runtime sử dụng HVM2
Các giới hạn và lưu ý hiện tại
- Bend tập trung vào việc mở rộng hiệu năng theo số lượng lõi, và được thiết kế để hỗ trợ hơn 10.000 thread đồng thời
- Phiên bản hiện tại có thể có hiệu năng đơn lõi thấp
- Hiệu năng được kỳ vọng sẽ cải thiện khi kỹ thuật sinh mã và tối ưu hóa phát triển hơn
- Hỗ trợ Windows vẫn đang được phát triển, và có thể dùng WSL2 như một giải pháp thay thế
- Hỗ trợ GPU hiện tại chỉ dành cho NVIDIA GPU
Cài đặt và cách chạy
- Cả Linux và Mac đều cần cài Rust
- Bản C của Bend dùng GCC, và README khuyến nghị GCC 12.x trở xuống
- Để dùng CUDA runtime, cần cài CUDA Toolkit 12.x cho Linux
- HVM2 được cài bằng
cargo install hvm, còn Bend được cài bằngcargo install bend-lang - Lệnh chạy chương trình Bend được chia theo từng backend thực thi
bend run <file.bend>: mặc định dùng trình thông dịch C, chạy song songbend run-rs <file.bend>: dùng trình thông dịch Rust, chạy tuần tựbend run-c <file.bend>: dùng trình thông dịch C, chạy song songbend run-cu <file.bend>: dùng trình thông dịch CUDA, chạy song song quy mô lớn
- Có thể biên dịch thành tệp C/CUDA độc lập bằng
gen-cvàgen-cu - Trình sinh mã vẫn đang ở giai đoạn đầu và chưa trưởng thành như các compiler như GCC hay GHC
- Có thể dùng cờ
-sđể xem số reduction, thời gian chạy và số interaction mỗi giây
Ví dụ cộng tuần tự và cộng song song
- Ví dụ phép cộng trong README so sánh hai cách viết mã cộng các số từ
startđếntarget - Phiên bản tuần tự có cấu trúc cộng
starthiện tại vào kết quả củaSum(start + 1, target)- Phép tính tiếp theo phụ thuộc vào kết quả cộng trước đó
- Không thể chuyển sang bước tiếp theo trước khi phép tính hiện tại kết thúc, nên không thể song song hóa
- Ví dụ gọi
Sum(1, 1_000_000), kèm chú thích rằng có thể vượt quá giá trị tối đa của kiểu số trong Bend
- Phiên bản có thể song song hóa chia đôi phạm vi rồi tính đệ quy tổng của nửa trái và nửa phải
- Phép tính
(3 + 4)không phụ thuộc vào phép tính(1 + 2) - Hai phép tính có thể diễn ra đồng thời nên có thể thực thi song song
- Phép tính
- Trong Bend, nếu mã có thể chạy song song thì chỉ cần đổi lệnh chạy là sẽ thực thi song song
Ví dụ hiệu năng của Bitonic Sorter
- README đưa ra bitonic sorter được triển khai bằng xoay cây bất biến như một ví dụ về tốc độ
- Đây là kiểu thuật toán vốn không dễ được kỳ vọng sẽ chạy nhanh trên GPU, nhưng Bend dùng cách tiếp cận chia để trị để chạy trên nhiều thread
- Không cần tạo thread hay quản lý lock một cách tường minh
- Kết quả benchmark như sau
bend run-rs: CPU, Apple M3 Max, 12.15 giâybend run-c: CPU, Apple M3 Max, 0.96 giâybend run-cu: GPU, NVIDIA RTX 4090, 0.21 giây
- Có thể xem các thuật toán khác trong examples folder
Tài liệu tham khảo
- Công nghệ nền tảng của Bend có thể xem trong paper về HVM2
- Tài liệu chính thức vẫn đang được hoàn thiện, và phần giải thích sâu hơn có trong GUIDE.md
- Danh sách tính năng có trong FEATURES.md
- Bend được phát triển bởi HigherOrderCO
1 bình luận
Ý kiến trên Hacker News
Tôi thử chuyển ví dụ
sumsang Python thuần thì vớipypy3mất 4,478 giây trên một luồng, còn Python 3.12 mất 1 phút 42,148 giây.Trong khi đó, phiên bản Bend một luồng đã chạy đến phút thứ 42 trên laptop của tôi, dùng 6GB bộ nhớ mà vẫn chưa kết thúc. Môi trường là 12th Gen Intel(R) Core(TM) i7-1270P, Ubuntu 24.04.
Nếu chậm như vậy ở một ví dụ rất đơn giản thì khó kỳ vọng gì ở các tác vụ phức tạp, và tôi tò mò liệu nó đã được kiểm thử hay phát triển trên môi trường ngoài Mac/aarch64 chưa. Sau này tôi định chạy lại với tham số
-s.Trong ví dụ
sum, Bend chịu bất lợi lớn là cấp phát 2 nút IC cho mỗi phép toán số, còn Python thì không. Chúng tôi dự định sớm tránh được điều này như HVM1, nhưng HVM2 hiện chưa triển khai.Phần lớn công việc với Bend đã dành cho việc làm cho bộ đánh giá song song hoạt động đúng, và việc chạy closure cùng đệ quy không giới hạn trên GPU là cực kỳ khó. Vì chúng tôi vừa mới hoàn tất phần đó nên gần như chưa đầu tư vào tối ưu vi mô, và phần sinh mã của HVM2 hiện vẫn còn rất tệ.
Nếu so với các ví dụ như
Bitonic Sort, nơi hai bên cấp phát lượng tương đương nhau, có thể sẽ thấy hiệu năng thực tế một cách công bằng hơn. HVM1 trên một lõi chậm hơn GHC khoảng 3 lần, và tôi nghĩ HVM2 cũng sẽ sớm đạt đến mức đó.Tôi hiểu rằng câu “hiện còn tệ nhưng sẽ tốt lên” có thể làm cụt hứng. Dù vậy, nền tảng đã xong, nên tối ưu vi mô là phần dễ nhất, và tôi tin hiệu năng sẽ tăng mạnh từ đây.
Ngôn ngữ này nhắm tới các ứng dụng GPU nặng về tính toán và vẫn đang ở giai đoạn đầu. Đệ quy không phải ứng dụng mục tiêu, và tôi nghĩ khó xem đây là benchmark phù hợp.
Nó giống việc ISPC có thể biên dịch để thực thi đồng thời 32 lời gọi hàm cho mỗi thread CPU. Ví dụ, nếu dùng dữ liệu 16-bit trên AVX512, có thể có 32 lõi × 2 SMT thread mỗi lõi × 32 thực thi do compiler tạo ra = 2048 thực thi diễn ra đồng thời.
Một cách triển khai kiểu Python có lẽ sẽ dùng vòng lặp và trạng thái có thể thay đổi.
+0. Chẳng phải đó là phép toán không làm gì sao?Có nhiều phản ứng tiêu cực trong thread này, nhưng chỉ riêng việc làm được đến mức này cũng khiến tôi muốn gửi kudos tới tác giả.
Trong các dự án tương tự, tôi chỉ biết đến Futhark, nhưng vì cú pháp kiểu Haskell nên có thể khá khó hiểu với lập trình viên phổ thông quen C/C++/Python/JS/Java, v.v.
Điều đáng tiếc nhất là khác với Futhark, nó chỉ nhắm tới CUDA hoặc multicore. Futhark có thể nhắm tới OpenCL, CUDA, ISPC, HIP, CPU một lõi, CPU đa lõi. Tôi nghĩ các vấn đề hiệu năng mà người khác chỉ ra hoàn toàn có thể giải quyết được.
Ví dụ ngắn: https://github.com/m4rs-mt/ILGPU/blob/master/Samples/SimpleM...
Nó cũng hỗ trợ các tính năng nâng cao như inline PTX assembly: https://github.com/m4rs-mt/ILGPU/blob/master/Samples/InlineP...
NVIDIA cũng đã tài trợ các biến thể Haskell, .NET, Java, Julia trên CUDA, có cả Python JIT, và họ cũng đang hợp tác với phía Mojo.
Nó được tạo ra bởi Tucker Taft, người tham gia thiết kế Ada từ năm 1995, và một số tính năng song song của ParaSail đã được đưa vào Ada 2022.
OP mang đến một trong những thứ hay nhất gần đây trên HN, nên tôi thấy tiếc khi có vẻ chỉ nhận toàn những lời phê bình dài, dù rõ ràng đây vẫn là phiên bản sơ khai.
Ngược lại, cách phê bình đúng thì có giới hạn, còn cách sai thì rất nhiều, nên phê bình có thể đa dạng vô tận. Vì vậy chỉ có vài bình luận tích cực, còn đa số trông như phê bình hoặc “giá mà cũng làm cái này”. Đây không hẳn là lỗi của cá nhân nào, mà gần với văn hóa kỹ sư ngày nay.
Nếu người ta chỉ giấu những sự thật phũ phàng sau tràng pháo tay thì thế giới sẽ sụp đổ.
Phê bình cũng có nghĩa là người ta quan tâm và tham gia vào ý tưởng cũng như cách tiếp cận, nên thường là một tín hiệu tích cực.
Nhưng phê bình các dự án đưa ra tuyên bố dễ gây hiểu lầm, thiếu căn cứ hoặc sai sự thật cũng là một chuẩn mực xã hội tốt. Vì nó khiến những tuyên bố như vậy giảm đi.
Thứ khó hiểu thường tạo cảm giác bị đe dọa, và phê bình là phản ứng phổ biến trước mối đe dọa, cũng là cách đáp lại cần ít hiểu biết nhất.
Trang chủ được làm thật sự rất tốt. Nhìn vào là thấy rõ ngay nó làm gì
Những người làm về “combinator” thường hay muốn dùng rất nhiều thuật ngữ chuyên môn đáng sợ, nhưng OP thực sự cho thấy ý tưởng đơn giản đằng sau công cụ. Mình thích vì nó trái ngược với kiểu tiếp cận học thuật: cho xem đến từng chi tiết cuối cùng nhưng lại không nói rốt cuộc chuyện gì đang diễn ra. Cần có nhiều cách làm như thế này hơn
Về lý thuyết thì rất hay và mình cũng hiểu đề xuất giá trị, nhưng nói thật là mình không nghĩ thứ này sẽ trở thành một công cụ có liên quan trong thực tế
Đây là ghi chú sau ấn tượng ban đầu và sau khi lướt qua bài báo. Mình biết đây là phần mềm ở giai đoạn rất sớm
Bend trông giống một DSL rất hạn chế. Không có FFI, không có cách tương tác với buffer nguyên thủy, và định dạng số thực dấu phẩy động 24-bit cũng kỳ lạ
Có lý do khiến IC không trở thành xu hướng chủ đạo. Hiệu năng nhiều khả năng sẽ tiếp tục tệ, và việc duyệt đồ thị không hợp với phần cứng
Giả định về optimal reduction là hợp lệ, nhưng rốt cuộc bạn vẫn phải viết kernel theo cách có thể song song hóa. Tức là không được có phụ thuộc dữ liệu và cũng phải tính đến việc dùng đệ quy
Không có ví dụ nghiêm túc nào so sánh trực tiếp mã Bend/HVM với chương trình OMP/CUDA tương đương. Rất khó đánh giá độ phức tạp triển khai giảm được bao nhiêu và hiệu năng ở mức nào
Trong tính toán song song hiệu năng cao ngoài đời thực, cấu trúc dạng cây hầu như không có, và mảng mới là vua. Đó là do các đặc tính vật lý của cách bộ nhớ hoạt động ở cấp phần cứng. Thứ phù hợp nhất với buffer bộ nhớ liên tục có thể thay đổi là vòng lặp. Nếu HVM triển khai được điều này thì mình sẽ theo dõi
Hiện tại nó gần như bị cô lập hoàn toàn với dữ liệu bên ngoài, rất chậm, và trông như một ngôn ngữ còn nửa vời đặt một lớp trừu tượng khổng lồ lên trên phần cứng. Nó cũng không tận dụng được các tính năng như cache nhiều tầng, tensor core, SIMD, thao tác nguyên tử
Xin lỗi nếu nghe hơi gay gắt, nhưng mình vẫn thấy phần triển khai kỹ thuật và nền tảng lý thuyết rất thú vị. Chỉ là mình vẫn chưa bị thuyết phục về tính hữu dụng của nó trong thế giới thực
FFI đã được triển khai rồi nhưng chưa công bố. Lý do là chúng tôi muốn ra mắt nó cùng với phần render đồ họa, và mình nghĩ nó sẽ khá ấn tượng
Haskell/GHC cũng dùng đồ thị và cây, nhưng không ai nói nó không thực dụng. Đúng là mảng là vua, nhưng rất nhiều thuật toán hiện đại không hợp với mảng, như compiler, type checker, solver, lại được triển khai bằng Haskell
Lý do chính khiến IC chưa nhanh là vì chưa có ai thực sự làm tốt phần tối ưu hóa cấp thấp trên nó. Tất cả các triển khai trước đây đều cực kỳ kém hiệu quả, và công việc của mình đến nay cũng chủ yếu dành thời gian để khiến nó chạy đúng trên GPU
Như chuyện hiện còn chưa có vòng lặp, lời giải chỉ đơn giản là thêm vòng lặp. Nếu bạn nghĩ ở đó có một giới hạn mang tính bản chất, có lẽ bạn sẽ ngạc nhiên
HVM2 cuối cùng đã trở thành một thuật toán đúng và có thể mở rộng, và giờ là lúc tối ưu hiệu năng cấp thấp thực sự
Trong các thuật toán Fast Multipole hoặc Barnes-Hut, người ta dùng thứ tự Morton hoặc thứ tự H-index để giảm các phép tính từng cặp O(n²) xuống lần lượt còn O(n), O(n log n). Barnes-Hut phổ biến hơn trong vật lý thiên văn, còn Fast Multipole thường thấy hơn trong động lực học phân tử hóa học
10 năm trước mình đã học 15-210, môn thuật toán song song ở CMU. Môn học giải thích rằng khi định luật Moore chạm giới hạn, tính song song sẽ là tương lai của điện toán, và điều đó thuyết phục mình muốn thử nghiệm
Nhưng không có nhiều lựa chọn lập trình song song đa dụng. Ngay cả SML dùng trong lớp cũng không song song, và ở cuối có một phần dùng extension và CUDA, nhưng theo mình nhớ thì khá hạn chế
Sau đó nhờ Rust mình đã thử nghiệm được một chút với multithreading, và nhờ Shadertoy mình có thể làm các việc sáng tạo bằng shader. Nhưng một ngôn ngữ song song đa dụng trên GPU thì mình rất háo hức được tự tay thử
Nếu bạn thích 210, có thể bạn cũng sẽ thích https://futhark-lang.org/. Đây là ngôn ngữ thuộc họ ML, biên dịch xuống GPU và có hiệu năng tốt
Ý tưởng rất hay, nhưng nếu mình không bỏ sót gì thì nó trông rất chậm
Mình viết một vòng lặp đơn giản bằng C++ để cộng từ 0 đến 2³⁰; không tối ưu, chạy đơn luồng trên laptop của mình mất 1,7 giây, tương đương hiệu năng của Bend trên RTX 4090. Thêm
-O3thì vòng lặp được vector hóa và chạy dưới 80msNếu so với một chương trình C thực sự thực hiện cấp phát, Bend có khả năng nhanh hơn chỉ với vài thread
Phần sinh mã của Bend hiện vẫn rất tệ, nhưng đây là những quả thấp dễ hái. Phần lớn công sức đã dồn vào việc làm cho bộ đánh giá song song rất khó kia chạy đúng
Mình biết nghe như kiểu “hãy tin tôi”, nhưng khi bắt đầu biên dịch thủ tục, sinh vòng lặp, v.v. thì hiệu năng đơn luồng sẽ tốt hơn nhiều. Chỉ là hiện vẫn chưa làm thôi
Thực ra mình cũng nghĩ có lẽ nên đợi thêm một chút trước khi đăng lên
objdumpxem vòng lặp có thật sự được vector hóa hay compiler đã tối ưu bỏ toàn bộVòng lặp đó gây tràn số nguyên có dấu, và trong C++ đây là hành vi không xác định. Compiler có thể hợp pháp cho ra bất kỳ kết quả nào
Để tránh điều này, cần khai báo
sumlàunsigned. Tràn số nguyên không dấu được định nghĩa rõ, và tối ưu hóa vẫn xảy ra nhưng ít nhất tính đúng đắn được bảo đảm-O3, vòng lặp bị loại bỏ hoàn toàn: https://godbolt.org/z/M1rMY6qM9Có lẽ đây không phải là so sánh công bằng
Tất nhiên mình cũng có thể đang hiểu sai ý chính
Muốn chúc mừng tác giả. Đây thật sự là một công trình rất tuyệt
Tạo ra tự động song song hóa đúng đắn không hề dễ, và bạn hoàn toàn có quyền tự hào. Mình mong chờ xem dự án sẽ phát triển ra sao
Tôi không hiểu vì sao lại có nhiều phản ứng tiêu cực như vậy. Trông giống như một đám đông giận dữ đang bới móc các lỗ hổng trong README, như những bot cố bẻ lái ngữ cảnh và ý định của bài viết
Tranh cãi hàng giờ liền mà không bỏ ra nổi 2 phút để đọc cho đúng thì thật thiếu hiểu biết và tàn nhẫn. OP đã đi được đến đây với một dự án một người, nên tôi mong họ tiếp tục đẩy tới
Tôi đã tò mò không biết HVM2 biên dịch các interaction net sang, ví dụ như SPIR-V, hay giống HVM gốc, là một interpreter chạy trên GPU
Trước đây tôi từng thử biên dịch interaction net sang C theo cách rút gọn chương trình càng nhiều càng tốt, nhưng không rút gọn input, và xử lý nó như tối ưu hóa toàn chương trình. Tôi nghĩ nhắm đến shader language cũng không quá khó
Nhìn vào repository thì thấy nói rằng nó cung cấp một ngôn ngữ IR cấp thấp để chỉ định các net HVM2 và một compiler sang C/CUDA: https://github.com/HigherOrderCO/HVM
Nhưng nhìn lại thì HVM2 CUDA runtime có vẻ giống một interpreter duyệt đồ thị trong bộ nhớ và áp dụng các bước rút gọn: https://github.com/HigherOrderCO/HVM/blob/5de3e7ed8f1fcee6f2...
Điều tôi nói đến là một cách duyệt interaction net để khôi phục các term gần với biểu thức lambda calculus, rồi hạ xuống C theo từng mảnh nhỏ nhằm tối thiểu hóa overhead runtime
Động cơ thực tế là Bend khó có thể đánh bại các GPU kernel viết tay trong những workload như ML. Về lý thuyết, HVM có thể đóng vai trò lớp keo ghép các kernel tính toán và song song hóa thứ tự thực thi, nhưng để làm vậy cần một FFI tốt
Interaction net khó dịch qua ranh giới FFI, nhưng nếu đặt các node kernel tính toán FFI bên trong interaction network và biên dịch net sang C, ta có thể khôi phục một FFI hợp lý mà không có overhead dịch
Một lựa chọn khác là triển khai HVM bằng phần cứng; tôi đang nghịch thử một chút trên FPGA còn dư
Không trực tiếp nhắm đến SPIR-V, nhưng đó là mục tiêu
Compiler C đem lại mức tăng tốc như kỳ vọng, tức 3–4 lần và sắp tới còn hơn nữa, nhưng CUDA runtime không đạt được mức tăng tốc đáng kể so với phiên bản không biên dịch
Tôi cho rằng nguyên nhân là warp divergence. Trong các procedure chưa biên dịch, mọi lời gọi hàm có thể được gộp vào một hàm mở rộng kiểu interpreter “đa dụng” duy nhất, và các thread trong warp có thể rút gọn mà không cần rẽ nhánh. Chúng tôi sẽ nghiên cứu sâu hơn phần này trong thời gian tới