Vấn đề lãng phí bộ nhớ của Lua có giải pháp khi tối ưu hóa mảng mới hướng tới bản phát hành tiếp theo

Nhóm Cộng đồng BigGo
Vấn đề lãng phí bộ nhớ của Lua có giải pháp khi tối ưu hóa mảng mới hướng tới bản phát hành tiếp theo

Lua , ngôn ngữ lập trình nhẹ phổ biến trong các hệ thống nhúng và phát triển game, đã âm thầm lãng phí gần một nửa bộ nhớ khi lưu trữ mảng. Một nghiên cứu học thuật gần đây đã tiết lộ sự kém hiệu quả này và đề xuất các giải pháp có thể sớm được đưa vào bản phát hành chính thức tiếp theo của ngôn ngữ.

Vấn đề xuất phát từ cách Lua biểu diễn các giá trị bên trong. Mỗi giá trị mang theo cả dữ liệu và thẻ loại, nhưng do yêu cầu căn chỉnh bộ nhớ, cách tiếp cận này lãng phí khoảng 40% không gian thông qua padding. Đối với các chương trình nhỏ, điều này có thể không quan trọng lắm. Nhưng đối với các ứng dụng có mảng lớn, nó có thể có nghĩa là sử dụng nhiều hơn 40% bộ nhớ so với mức cần thiết.

Lãng phí bộ nhớ hiện tại trong mảng Lua:

  • Lãng phí do padding: ~40% bộ nhớ bị mất do khoảng trống căn chỉnh
  • Kích thước cấu trúc TValue: 16 byte (hệ thống 64-bit)
  • Sử dụng dữ liệu thực tế: Chỉ ~60% bộ nhớ được cấp phát
  • Tác động: Đặc biệt nghiêm trọng đối với các mảng lớn có hàng nghìn phần tử

Thách thức kỹ thuật đằng sau sự lãng phí

Lua sử dụng cái mà các nhà nghiên cứu gọi là tagged values - mỗi phần dữ liệu được đi kèm với thông tin về loại của nó. Lựa chọn thiết kế này làm cho ngôn ngữ linh hoạt và động, cho phép các biến thay đổi loại trong thời gian chạy. Tuy nhiên, việc triển khai hiện tại lưu trữ các tagged values này theo cách tạo ra khoảng trống trong bộ nhớ do các hạn chế căn chỉnh.

Hãy nghĩ về nó như việc đóng gói các hộp trong một chiếc xe tải, nơi mỗi hộp phải được đặt ở các khoảng cách cụ thể, để lại khoảng trống giữa chúng. Những khoảng trống này cộng dồn nhanh chóng khi bạn đang xử lý hàng nghìn phần tử mảng.

Hạn chế căn chỉnh: Các quy tắc yêu cầu dữ liệu được lưu trữ tại các địa chỉ bộ nhớ cụ thể để truy cập bộ xử lý tối ưu

Cộng đồng tranh luận về giải pháp đơn giản so với tối ưu

Cộng đồng lập trình đã thảo luận về việc liệu Lua nên áp dụng các bản sửa lỗi đơn giản hơn hay các tối ưu hóa phức tạp hơn. Một số nhà phát triển chỉ ra rằng một thuộc tính compiler được gọi là __attribute__((packed)) có thể giải quyết vấn đề ngay lập tức, giảm việc sử dụng bộ nhớ từ 16 byte xuống 9 byte cho mỗi giá trị mà không mất hiệu suất.

Tuy nhiên, những người duy trì Lua ưu tiên tính di động hơn tất cả. Thuộc tính này chỉ hoạt động với một số compiler nhất định và không phải là một phần của C tiêu chuẩn, khiến nó không phù hợp cho một ngôn ngữ được thiết kế để chạy ở mọi nơi từ các máy chủ cao cấp đến các vi điều khiển cơ bản.

Cuộc tranh luận làm nổi bật một căng thẳng cơ bản trong thiết kế ngôn ngữ giữa tối ưu hóa và tương thích. Như một thành viên cộng đồng lưu ý, cơ sở người dùng của Lua bao gồm nhiều hệ thống nhúng với phần cứng bất thường và các compiler cũ không hỗ trợ các phần mở rộng hiện đại.

So sánh các giải pháp được đề xuất:

  • Thuộc tính packed: Giảm kích thước từ 16 xuống 9 byte, nhưng không có tính di động
  • Mảng song song: Loại bỏ hoàn toàn padding, duy trì tính di động
  • Mảng phản chiếu: Không lãng phí padding, không có overhead bổ sung cho các mảng nhỏ
  • Mảng đồng nhất: Một thẻ kiểu duy nhất cho mỗi mảng, nhưng tăng thêm độ phức tạp

Giải pháp chiến thắng: Reflected Arrays

Trong số các phương án được đề xuất, cách tiếp cận Reflected Arrays dường như đang hướng tới việc triển khai trong bản phát hành tiếp theo của Lua . Kỹ thuật này hoàn toàn loại bỏ lãng phí padding bằng cách lưu trữ các giá trị và thẻ loại của chúng trong các mảng riêng biệt, song song.

Thay vì giữ mỗi giá trị bên cạnh thẻ của nó, hệ thống lưu trữ tất cả các giá trị cùng nhau trong một khối và tất cả các thẻ cùng nhau trong một khối khác. Khi chương trình cần truy cập một phần tử mảng, nó lấy giá trị từ một vị trí và thẻ từ một vị trí tương ứng trong mảng thẻ.

Cách tiếp cận này duy trì khả năng tương thích với mã hiện có trong khi cung cấp việc tiết kiệm bộ nhớ mà các ứng dụng lớn rất cần. Giải pháp hoạt động trên bất kỳ hệ thống nào có thể chạy Lua hiện tại, khiến nó trở thành sự phù hợp hoàn hảo cho các yêu cầu di động của ngôn ngữ.

Tác động thực tế đối với các ứng dụng

Việc tối ưu hóa bộ nhớ có thể mang lại lợi ích đáng kể cho các ứng dụng phụ thuộc nhiều vào mảng. Các game, công cụ xử lý dữ liệu và ứng dụng tính toán khoa học được viết bằng Lua sẽ thấy việc giảm sử dụng bộ nhớ ngay lập tức mà không cần thay đổi mã.

Cải tiến trở nên đặc biệt có giá trị trong các môi trường hạn chế bộ nhớ như hệ thống nhúng, nơi mỗi byte đều quan trọng. Đối với các nhà phát triển làm việc trên game di động hoặc thiết bị IoT , tối ưu hóa này có thể có nghĩa là sự khác biệt giữa việc phù hợp ứng dụng của họ trong bộ nhớ có sẵn hoặc phải thiết kế lại cách tiếp cận của họ.

Mặc dù các chi tiết kỹ thuật có thể có vẻ trừu tượng, tác động thực tế là đơn giản: các chương trình Lua sẽ sử dụng ít bộ nhớ hơn trong khi chạy nhanh như nhau, khiến ngôn ngữ này trở nên hấp dẫn hơn nữa đối với các ứng dụng nhạy cảm với tài nguyên.

Tham khảo: Compact Representations for Arrays in Lua