[Tiếng Việt] Thủ thuật với C++ - Vì sao MSVC, ICC không có VLA
Tại sao MSVC lại không support VLA?
Trên thực tế VLA thuộc về chuẩn C99 (WG14/N683) và nó không phải là thuộc chuẩn C++98. Dù rằng nó từng được xem xét khi chuẩn C++11 (WG21/N2648) và C++ 14 (WG21/3690) xuất hiện. Tuy nhiên nó đã không bao giờ và sẽ không bao giờ tồn tại trên C++.
Giờ thì lại nói về MSVC. MSVC thực tế từ trước đây nó là một compiler C, sau đó Microsoft thấy rằng việc họ ứng dụng lập trình hướng đối tượng giúp ích rất nhiều. Sau đó họ chuyển compiler MSVC sang trọng tâm là C++98. Vì lý do đó, không phải bất kỳ tính năng nào của C99 được thêm vào MSVC, chỉ có tính năng phù hợp mới được đưa vào.
Điều này tương tự Intel với ICC.
Vậy tại sao Clang, GCC lại có?
Bản thân các compiler này là compiler C/C++ chứ không phải là compiler tiêu chuẩn C++ như MSVC và ICC. Các compiler này tuân theo chuẩn POSIX, tức là họ giữ nguyên khả năng tương thích AT&T C compiler từ xưa chứ ít thay đổi.
Vậy tại sao giới lập trình từ bỏ VLA?
Chúng ta hãy xem xét ví dụ sau:
Như ta thấy nếu không khai báo giá trị n thì giá trị trong trường hợp này là bao nhiêu? Mảng a sẽ có bao nhiêu phần tử? Như ta thấy việc quên gán giá trị vào sẽ khiến cho giá trị mảng phình to ra và tốn rất nhiều bộ nhớ.
Điểm mạnh của VLA chính là việc VLA được khai báo và sử dụng trên stack cho phép tốc độ truy xuất dữ liệu rất nhanh do là contiguous memory allocation (bộ nhớ cấp phát tĩnh liên tục). Điểm mạnh cũng là điểm yếu, stack có giới hạn về kích thước. Đồng thời việc các hệ thống từ năm 2000 trở về sau sử dụng rất nhiều thread (xử lý luồng) cho thấy điểm yếu của VLA. Mỗi thread sinh ra sử dụng mỗi stack riêng. Như vậy số bộ nhớ dùng sẽ tăng rất nhiều, chưa kể đến việc dữ liệu trên mỗi stack sẽ khác nhau gây ra tình trạng sai lệch.
Còn việc dùng các phương pháp thay thế khác như non-contiguous memory allocation (bộ nhớ cấp phát động không liên tục) thì sửa dụng heap chia sẻ giữa các thread nên việc đồng bộ dữ liệu diễn ra trơn tru hơn.
Ta xem xét một ví dụ khác
Việc dùng khởi tạo dùng con trỏ cho phép kiểm tra xem có đủ bộ nhớ để có thể tạo ra mảng hay không. Sử dụng VLA sẽ không thể nào kiểm tra được.
Trong lập trình nhúng, việc sử dụng multi thread rất ít khi được sử dụng do đó VLA vẫn xuất hiện nhiều. Còn trong lập trình hệ thống, ngay cả Linus Torvalds còn có cái nhìn rất cực đoan về VLA.
Các bạn trẻ còn ngồi trên ghế nhà trường như học sinh, sinh viên có thể dùng VLA thoải mái (đi thi cuối kỳ ở trường hay Olympic/ACM ICPC) nhưng khi bắt đầu sự nghiệp lập trình chuyên nghiệp dùng VLA khiến cho chương trình của bạn dễ phát sinh lỗi bất kỳ lúc nào.
Tìm cách thay thế nó
Trong C có lẽ chỉ có thể dùng mảng cấp phát tĩnh với hằng số. Hoặc dùng con trỏ kết hợp malloc, alloca, ...
Trong C++ mọi thứ có vẻ dễ thở hơn khi có thêm các lựa chọn khác như STL Vector. Ngoài ra có thể dùng tạo mảng động với con trỏ và new, delete.
Vậy bây giờ mình muốn tạo mảng 2 chiều có m cột n dòng, thì mình sẽ tạo 1 mảng 1 chiều có chiều dài là m*n. Khi tính toán thì sẽ xử lý từ m đối tượng trước, sau đó xử lý m đối tượng tiếp theo. Việc này có vẻ cực nhọc hơn nhưng bù lại chương trình sẽ tốt hơn.
Lưu ý: các hàm cấp phát động như malloc, alloca, ... cũng khá nguy hiểm. Trước khi khai báo phải xác định rõ số lượng biến cần thiết để khai báo. Sau khi dùng xong thì cần phải free. Ưu điểm duy nhất có lẽ là dùng heap chia sẻ được vùng nhớ.
Cảnh báo khi dùng VLA trong code
Trong Clang và GCC (phiên bản mới từ 2011 trở về sau) có hỗ trợ flag (cờ) để thông báo developer rằng trong code có VLA
Vấn đề khi dùng các phương pháp thay thế
Như mình đã nói ở dòng trên việc dùng VLA sẽ cho phép chạy nhanh hơn, các tùy chọn kia là bộ nhớ cấp phát động việc truy xuất cũng diễn ra chậm hơn. Do đó, trò chơi mà bạn Belarus port sau khi thay thế VLA bằng vector thì hiệu năng cực kỳ thảm họa. Các animation chuyển động khá chậm khi render từ 10 cầu thủ render trở lên trong 1 khung hình (2 bên có tổng cộng 22 người, nhưng khung hình chỉ render 10). Việc càng nhiều cầu thủ, số lượng animation tăng lên dẫn đến việc nghẽn xử lý tính toán. Điều này có thể khắc phục khi dùng hệ thống tính toán khác tăng tốc xử lý lên.
Như mình đã nói, VLA tuy nhanh, nhưng cái giá phải trả cũng rất là lớn. Do đó không có lý do gì mà bạn vẫn dùng VLA cả. Vẫn có cách khiến cho chương trình của bạn chạy nhanh hơn.
Comments
Comments are closed