1 điểm bởi GN⁺ 2 giờ trước | 1 bình luận | Chia sẻ qua WhatsApp
  • Bên trong zig build được tách thành các tiến trình configurer và maker, còn đồ thị build do build.zig tạo ra được tuần tự hóa thành một tệp cấu hình nhị phân
  • Tiến trình cha lưu vào bộ nhớ đệm tệp cấu hình và biên dịch maker bất đồng bộ ở chế độ Release; maker chỉ cần được biên dịch một lần vào bộ nhớ đệm toàn cục cho mỗi phiên bản Zig
  • Khi người dùng thay đổi build.zig, không còn phải biên dịch lại toàn bộ hệ thống build cùng lúc, giúp giảm chi phí thời gian khi bổ sung các tính năng như --watch, --fuzz, --webui
  • Thời gian chạy trung bình của zig build --help giảm từ 150ms xuống 14.3ms, còn số chu kỳ CPU, số lệnh và truy cập bộ nhớ đệm cũng giảm trong khoảng 94~96%
  • Phần lớn API vẫn tương thích, nhưng việc quan sát trực tiếp b.args được thay bằng addPassthruArgs(), và Zig 0.17.0 dự kiến sẽ ra mắt trong vài tuần tới

Thay đổi cấu trúc hệ thống build

  • Nhánh lớn đã được hợp nhất, tách nội bộ zig build thành tiến trình configurertiến trình maker
  • Trong cấu trúc cũ, tệp build.zig và toàn bộ phần triển khai của hệ thống build được biên dịch thành một tiến trình lớn ở chế độ Debug, rồi build.zig tạo đồ thị build trong bộ nhớ trước khi “build runner” thực thi nó
  • Trong cấu trúc mới, build.zig được biên dịch thành một tiến trình configurer nhỏ ở chế độ Debug, và đồ thị build được tuần tự hóa thành một tệp cấu hình nhị phân
  • Tiến trình cha zig build phát hiện tệp cấu hình, lưu vào bộ nhớ đệm cho lần chạy tiếp theo, đồng thời biên dịch bất đồng bộ maker ở chế độ Release để đảm nhiệm việc thực thi đồ thị build
  • Khi tệp cấu hình và quá trình biên dịch maker đã sẵn sàng, maker sẽ nhận tệp cấu hình và thực thi; nhờ bộ nhớ đệm toàn cục, maker chỉ cần được biên dịch một lần cho mỗi zig version

Tác động của việc cải thiện tốc độ

  • Mục tiêu cốt lõi là tăng tốc zig build, và khi người dùng thay đổi build.zig, sẽ không còn phải biên dịch lại toàn bộ hệ thống build cùng lúc
  • Khi các tính năng như --watch, --fuzz, --webui được đưa vào, điều này càng có ý nghĩa vì thời gian của zig build sẽ không tăng theo số lượng tính năng của hệ thống build
  • Nếu xác định không có thay đổi, có thể tái sử dụng cấu hình trước đó mà không cần chạy lại logic build.zig
  • Ví dụ, ngay cả khi thêm -freference-trace vào dòng lệnh zig build, cũng không cần chạy lại logic build.zig một cách không cần thiết
  • Vì tiến trình thực thi đồ thị build thực tế được biên dịch với tối ưu hóa được bật, nên giai đoạn thực thi cũng nhanh hơn

Kết quả benchmark

  • Thời gian chạy trung bình của zig build --help đã giảm từ 150ms trong cấu trúc cũ xuống còn 14.3ms trong cấu trúc mới
  • Thời gian đồng hồ thực giảm từ 150ms xuống 14.3ms, tức giảm 90.4%; số chu kỳ CPU giảm từ 593M xuống 24.1M, tức giảm 95.9%
  • Số lệnh giảm từ 995M xuống 43.7M, tức giảm 95.6%; số lần truy cập bộ nhớ đệm giảm từ 25.8M xuống 1.46M, tức giảm 94.3%
  • Peak RSS giảm từ 84.8MB xuống 78.5MB, tức giảm 7.4%
  • Khác biệt lớn nhất đến từ việc chuyển từ mô hình chạy logic build.zig cho mọi lệnh zig build sang mô hình tái sử dụng cấu hình tuần tự hóa đã được lưu đệm

Ảnh hưởng đến công cụ và khả năng tương thích

  • Các công cụ bên thứ ba như ZLS có thể hưởng lợi bằng cách tiêu thụ trực tiếp tệp cấu hình đã tuần tự hóa thay vì fork và duy trì build runner
  • Dù cơ chế nội bộ đã thay đổi đáng kể, xét từ góc độ API thì phần lớn vẫn giữ tương thích
  • Các ngoại lệ tương ứng với những thay đổi đã được tổng hợp trong PR đã hợp nhất

Những thay đổi phá vỡ chính

  • Thay đổi mà nhiều người dùng có khả năng gặp nhất là việc loại bỏ kiểu dùng quan sát trực tiếp b.args
  • Mã cũ:
if (b.args) |args| {
    run_cmd.addArgs(args);
}
  • Mã mới:
run_cmd.addPassthruArgs();
  • Với thay đổi này, script build sẽ không còn quan sát được các đối số đó nữa, tức là một tính năng đã bị loại bỏ
  • Đổi lại, dù thay đổi các đối số đó thì cũng không còn cần phải biên dịch lại script build từ mã nguồn

Kiểm thử và lịch phát hành

  • Người dùng muốn tác động đến định hướng của Zig có thể nâng dự án lên phiên bản phát triển để thử các thay đổi mới và gửi phản hồi
  • Zig 0.17.0 dự kiến sẽ được phát hành trong vài tuần tới
  • Do không có thời gian để thử trước, nên ngay cả khi build bị hỏng ở 0.17.0, vẫn còn đủ cơ hội để đưa bản sửa vào tag 0.17.1

1 bình luận

 
Ý kiến trên Hacker News
  • Tôi đã thử chuyển một phần code sang Zig 0.16.0 và kết quả khá đáng hài lòng
    Phần bị ảnh hưởng là khá nhiều, nhưng bản thân các thay đổi đều tốt và có vẻ đi theo hướng làm tương lai của ngôn ngữ sáng sủa hơn
    Đặc biệt, cơ chế nhập/xuất mới cho phép viết mã hiệu quả với hình thức đẹp mắt trong cả triển khai đơn luồng, đa luồng lẫn vòng lặp sự kiện
    Nếu từ sau 0.16.0 bạn vẫn chưa dùng lại Zig thì rất nên xem qua. Ghi chú phát hành lần này dài kinh khủng
    https://ziglang.org/download/0.16.0/release-notes.html

    • Tôi vẫn chưa cho rằng nó “rất hiệu quả”. Io vẫn là dynamic dispatch và có nhiều tầng tham chiếu gián tiếp
      Theo tôi biết thì nó còn chậm hơn trước
      Hy vọng các bản phát hành tới sẽ xử lý vấn đề “đích dispatch được biết ở thời điểm biên dịch nhưng vẫn còn động”, từ đó giảm thất thoát hiệu năng
    • Tôi thấy khá khó hiểu cơ chế nhập/xuất mới hoạt động thế nào, đặc biệt là async/await
      Tôi đoán rằng khi gọi io.async trong một triển khai IO dựa trên vòng lặp sự kiện, bên trong nó sẽ khởi động thứ gì đó như một “task”, và future sẽ là handle của nó
      Điều tôi không hiểu là khi gọi future.await(io) thì chuyện gì xảy ra. Triển khai IO có cách nào đó tạm dừng hàm hiện tại rồi tiếp tục lại khi future được giải quyết không? Nếu vậy thì có nghĩa mọi hàm trong Zig đều là coroutine không stack sao?
    • Có lẽ rồi cũng sẽ dùng được các tính năng mới như thế này, nhưng vì tính ổn định và các target ít được kiểm thử hơn nên tôi vẫn dùng .use_llvm = true trong bản build Zig
  • Sau vài tháng dùng Zig, tôi tin chắc đây là một ngôn ngữ tuyệt vời để làm công cụ
    Nó rất hợp để cầm lên và nhanh chóng ráp thử ý tưởng một cách tự do, và ở mỗi chỗ bị vướng thì những người tạo ra nó thường đã nghĩ sẵn một lời giải thoải mái
    Nhưng nó cũng không ép bạn phải dùng ngôn ngữ theo kiểu “đúng chuẩn”
    Giờ nó đã trở thành ngôn ngữ mặc định của tôi cho việc “mày mò trong gara”

    • Nói là không ép cách dùng “đúng chuẩn” thì hơi khó tin, vì nó không cho phép biến không sử dụng và cũng không có chú thích nhiều dòng
      Với tôi đây là vấn đề năng suất khá lớn
    • Nó thật sự tốt đến vậy à?
      Ngôn ngữ mặc định của tôi để “mày mò trong gara” là Python. Cú pháp nhẹ, cảm giác sử dụng không vướng víu, thư viện chuẩn phong phú, thiếu gì thì package cũng có hết
      Điểm mạnh của Zig là gì?
    • Trở ngại lớn nhất của tôi ở điểm này là với tôi, Mojo có vẻ mới là ngôn ngữ mặc định để thử nghiệm
    • Chắc chắn đây là một ngôn ngữ thử nghiệm rất tốt, nhưng đội ngũ và cộng đồng Zig có gu rất mạnh về cách dùng ngôn ngữ cho “đúng”
  • Xem video phỏng vấn Andrew Kelley xong tôi thấy muốn học thử Zig: https://www.youtube.com/watch?v=iqddnwKF8HQ

    • Tôi thật sự tôn trọng Andrew và cũng thích dùng Zig, nhưng cuộc phỏng vấn đó tệ khủng khiếp
      Câu trả lời của Andrew thì ổn, nhưng bầu không khí chung mang cảm giác tâng bốc quá mức
  • Tôi từng muốn dùng Zig, nhưng ngôn ngữ này vẫn thay đổi quá nhanh
    Mỗi bản phát hành đều phá vỡ API, nên rất khó vừa học ngôn ngữ, vừa gỡ lỗi hệ thống build, vừa theo kịp việc hiện thực hóa thứ mình thật sự muốn làm
    Sau khi xem phỏng vấn của JetBrains tôi cũng muốn thử lại, nhưng có lẽ sẽ đợi tới khi ra 1.0

  • Từ lâu tôi đã nghĩ về một ý tưởng mà tôi gọi là lập trình kép
    Đó là cách tổ chức stack chỉ với đúng hai ngôn ngữ: một ngôn ngữ bậc cao và một ngôn ngữ bậc thấp
    Càng viết được nhiều bằng ngôn ngữ bậc cao càng tốt, và chỉ hạ xuống ngôn ngữ bậc thấp khi thật sự cần
    Vấn đề là nếu bạn chưa thực sự rất giỏi ngôn ngữ bậc thấp đó, thì trước khi làm phần việc mức thấp, rất có thể bạn sẽ phải làm quen lại với ngôn ngữ ấy
    Vì thế C++ hay Rust trở nên khó viết hơn C, và với tôi C là lựa chọn mặc định. Nhưng C cũng có những vấn đề đã quá rõ
    Zig có vẻ lấp đúng điểm ngọt đó: đủ đơn giản để dễ nhặt lại sau một quãng nghỉ dài, nhưng vẫn có các công cụ hiện đại giúp việc lập trình dễ hơn

    • Ý tưởng đó thú vị, nhưng tôi lại nghĩ ngược lại
      Tôi muốn làm được càng nhiều càng tốt ở ngôn ngữ bậc thấp, và chỉ nâng lên ngôn ngữ bậc cao khi sự tiện lợi đáng để trả giá
      Roc cho phép làm điều này. Mọi chương trình đều có một platform được viết bằng ngôn ngữ bậc thấp, còn chương trình Roc thì dùng API mà platform đó phơi ra
      https://www.roc-lang.org/
      Tất nhiên việc cân bằng giữa bậc cao và bậc thấp thế nào thì mỗi người tự chọn
    • Theo tôi biết thì SpaCy được phát triển theo kiểu đó
      Các model được hiện thực bằng Cython, còn API cho người dùng thì cung cấp bằng Python
    • C# khá gần với mục tiêu đó
    • Cứ dùng một ngôn ngữ như Rust, thứ làm tốt cả lập trình bậc cao lẫn bậc thấp, là xong
      Giờ tôi làm mọi thứ bằng Rust, và đặc biệt nhờ hệ thống kiểu kiểu OCaml nên tôi vẫn chưa tìm ra thứ gì mình không làm được
    • Một tổ hợp kiểu này từng rất phổ biến là C và Lua
      Lua vốn được thiết kế như ngôn ngữ nhúng nên rất hợp cho khả năng tương tác, đồng thời cũng đủ bậc cao để dễ dùng
      Có lý do để Factorio dùng Lua làm ngôn ngữ scripting
  • Điều tôi thích ở quá trình phát triển Zig là họ đổ lượng công sức đáng kinh ngạc vào công cụ và vòng phản hồi cho lập trình viên, hơn là chỉ thêm tính năng ngôn ngữ
    Một ngôn ngữ mới vẫn có thể sống sót một thời gian dù thiếu đi một tính năng nào đó
    Nhưng nếu mỗi lần biên dịch, liên kết, cập nhật phụ thuộc đều thấy chậm chạp, thì khả năng sống sót sẽ khó hơn nhiều
    Việc tập trung biến chu kỳ phát triển từ đơn vị giây xuống mili giây có vẻ là một khoản đặt cược tốt về lâu dài

  • Tôi ngạc nhiên khi thấy câu “dự định phát hành 0.17.0 trong vài tuần tới”
    0.16 chẳng phải đã mất hơn một năm sao?
    Tôi không ngờ 0.17 lại ra nhanh như vậy, và hôm nay biết được điều này thật sự rất vui

  • Nghe có vẻ là tin tốt. Thời gian biên dịch của Zig vốn đã rất tốt, và có vẻ thay đổi này sẽ còn cải thiện hơn nữa

    • Theo kinh nghiệm của tôi thì nhận định đó hiện tại vẫn chủ yếu là gần với mục tiêu hơn thôi
      Đây rõ ràng là một mục tiêu quan trọng, và các cột mốc để đạt được cũng đã rõ, nhưng trên thực tế rất khó gọi khoảng thời gian chờ đầy đau đớn khi biên dịch lần đầu một project trống, hoặc khi ZLS build lại sau direnv allow, là “rất tốt”
    • Chỉ cần tạo một file có bài test giả rồi chạy zig test file.zig -OReleaseSafe thì trên máy tôi cũng mất vài giây
      Mỗi lần sửa file lại tiếp tục mất từng đó thời gian. Tôi đang dùng 0.16 hoặc master nên cũng không phải do toolchain quá cũ, và môi trường là Linux
      Bản thân ngôn ngữ Zig thực sự rất dễ dùng, nhưng tôi thấy compiler và thư viện chuẩn chưa được phát triển đủ theo hướng bảo thủ
      Khi bắt đầu với hello world thì có thể bạn chưa gặp vấn đề này, nhưng nếu làm fuzz test hay benchmark thì bạn sẽ muốn chạy binary đã tối ưu hóa
      Và rồi việc biên dịch ngay cả một lượng code tương đối nhỏ cũng trở nên quá bực bội
      Để so sánh thì tôi nghĩ Zig với tư cách một ngôn ngữ tốt hơn hẳn Rust/C++/C, nhưng trong Rust/C++/C kiểu vấn đề này thực tế hầu như không xảy ra. Với C/C++ thì giả định là dùng clang/gcc/ninja
      Trên cùng một máy, tôi có thể cấu hình, build (-O2 hoặc -O3), test một project C++ khoảng 10 nghìn dòng bằng Ninja/Python/clang chỉ trong 200ms
    • May mà tốc độ biên dịch kiểu thập niên 90 đang dần quay trở lại
  • Sẽ rất tuyệt nếu Zig có cơ chế chính thức để xuất Linux library stubs
    Khả năng cross-compile và nhắm tới các phiên bản glibc tùy ý của Zig đúng là phép màu thuần túy
    Tôi đang tận dụng phép màu này trong một hệ thống build C++ riêng, nhưng để lấy được các library stub đó từ Zig thì phải đi đường vòng
    Sẽ tốt hơn nếu nó được cung cấp như một đầu ra chính thức

  • Rốt cuộc thì có lý do gì để muốn dùng cái này thay vì Node.js và TypeScript?

    • Tôi ghen tị với kiểu suy nghĩ này. Nếu tôi cũng được như vậy thì chắc giờ đã phát hành nhiều app rồi
    • Hãy thử dùng Ghostty viết bằng Zig một chút, rồi so với các terminal như Hyper viết bằng JavaScript
    • Chẳng phải hai thứ đó phục vụ những lĩnh vực hoàn toàn khác nhau sao?
    • Nếu công việc bạn làm vốn rất hợp với Node và TypeScript, thì ngoài chuyện học hỏi hay tò mò ra, tôi không thấy có lý do gì để dùng Zig
      Nếu bạn không cần vắt kiệt hiệu năng, hay bố trí và kiểm soát bộ nhớ ở mức thấp, thì dùng Zig lại có nhiều điểm bất lợi hơn
      CRUD, công việc kiểu “enterprise”, hay website gần như không hưởng lợi gì từ Zig
    • Trong hệ thống nhúng thì bộ nhớ thiếu đến mức không thể chạy Node
      Một chương trình Zig đã biên dịch có thể chỉ vài KB và không cần dependency
      Truy cập mảng viết bằng ngôn ngữ cấp thấp có thể được tối ưu bằng SIMD và song song hóa, và có thể nhanh hơn cùng tác vụ viết bằng JavaScript nhiều bậc độ lớn
      Khác biệt này rất lớn trong xử lý văn bản, chỉnh sửa ảnh, xử lý video, hashing, v.v.
      Lý do để không dùng JavaScript thì thực tế là vô số