Memory layout và optimize Swift memory khi sử dụng Struct

Là lập trình viên iOS chắc không ai là không biết Struct. Nó là kiểu cấu trúc dữ liệu dạng value type. Tuy nhiên để sử struct mà tối ưu lượng memory được sử dụng thì không phải ai cũng biết.

1. Memory alignment

Chương trình máy tính được chạy sẽ bao gồm rất nhiều thao tác, trong đó có thao tác CPU lấy dữ liệu từ RAM lên để xử. Thi để CPU lấy dữ liệu từ RAM được hiệu quả thì các dữ liệu nằm trên RAM phải đảm bảo được đặt tại các địa chỉ theo đúng quy tắc. Ví dụ kiểu dữ liệu có 8 byte thì phải đặt ở địa chỉ chia hết cho 8, hay kiểu dữ liệu có độ dài 4 byte như Int32 phải đặt ở địa chỉ chia hết cho 4. Tương tự đối với các kiểu dữ liệu có độ dài khác, phải đặt tại vị trí mà địa chỉ chia hết cho độ dài của kiểu dữ liệu đó.

2. Swift memory layout

Khi ngôn ngữ swift, rất dễ dàng để chúng ta có thể kiểm tra được memory layout của một struct hay một class nào đó bằng cách sử dụng MemoryLayout.

MemoryLayout có 3 thông tin là stride, alignment và size.

  • Size: Đây là giá trị biểu thị kích thước của object.
  • Stride: Đây là giá trị biểu thị khoảng cách giữa 2 instance trong bộ nhớ nếu chúng nằm trong mảng. Đây được coi là bộ nhớ thực sự chương trình dành cho instance này.
  • Alignment: Đây là giá tri biểu thị độ dài lớn nhất của tất cả các trường trong kiểu dữ liệu.

Cùng định nghĩa 1 struct như sau.

Thử dùng MemoryLayout để xem nó như nào.

Phải rồi, có mỗi biến Bool thì tất cả sẽ là 1.

Thử thay đổi 1 chút, thêm vào 2 biến xem sao.

Sau khi thử bằng MemoryLayout thì có kết quả sau.

Ối giời. dùng tận 32 byte rồi.

Vậy tại sao lại dùng tận 24 byte nhỉ. Chẳng phải Bool 1 byte, Int 8 byte, Bool tiếp là 1 byte. Phải là 10 chớ 😅

Nói thêm về struct, thì đây là dữ liệu sẽ được truy cập tuần tự từ biến đầu tiên tới biến cuối cùng. Các biến được sắp xếp theo đúng thứ tự như chúng ta định nghĩa.

M
Mô tả cách layout

Như phần trước của bài này mình có nói về vấn đề memory alignment. Thì ở đây biến isActive sẽ được đặt đầu tiên ở địa chỉ với offset 0x0. Tiếp theo là biến age có độ dài 8 byte tại offset 0x8, từ 0x1 tới 0x7 được gọi là padding để giữ cho biến age được nằm đúng vị trí như memory alignment đã quy định. Tiếp theo là biến isAdmin, và những byte còn lại sẽ được làm padding.

Nếu chúng ta thay đổi lại 1 chút thứ tự các biến như sau thì sao:

😎

Thật tuyệt vời, chúng ta đã tiết kiệm được 8 byte cho struct User. Bộ nhớ lúc này đã được layout như sau.

Như trên hình thì biến age sẽ được đặt ở vị trí offset 0x0, isActive vị trí offset 0x8isAdmin ở offset 0x9.

3. Thử thêm 1 ví dụ

Thử tạo 10 triệu object để kiểm chứng những điều trên xem sao nhỉ 🙂

Trường hợp không optimize dùng 252M
Trường hợp optimize dùng 176M

4. Tổng kết

Thật không ngờ thay đổi vị trí các phần tử mà cũng có thể tối ưu lượng bộ nhớ RAM sử dụng khi dùng Struct. Vậy tiếc gì 1 chút thời gian để hiểu và thay đổi 1 chút để có thể tiết kiệm được nhiều RAM hơn, đặc biệt là các kiểu dữ liệu mà chúng ta khởi tạo nhiều trong chương trình.

Bật mí thêm 1 chút là struct trong C/C++ cũng vậy nhé các bạn.

Tham khảo thêm:

https://stevenpcurtis.medium.com/memorylayout-in-swift-c4e70bb32e3f
https://www.cs.umd.edu/~meesh/cmsc411/website/projects/outer/memory/align.htm

One Reply to “Memory layout và optimize Swift memory khi sử dụng Struct”

  1. Very nice post. I just stumbled upon your blog and wanted to say that I’ve really enjoyed browsing your blog posts. In any case I’ll be subscribing to your feed and I hope you write again soon!

Leave a Reply