Dấu chân bộ nhớ của các kiểu dữ liệu Haskell


124

Làm cách nào để tìm dung lượng bộ nhớ thực tế cần thiết để lưu trữ giá trị của một số kiểu dữ liệu trong Haskell (chủ yếu là với GHC)? Có thể đánh giá nó trong thời gian chạy (ví dụ: trong GHCi) hoặc có thể ước tính yêu cầu bộ nhớ của một kiểu dữ liệu phức hợp từ các thành phần của nó không?

Nói chung, nếu yêu cầu bộ nhớ của các kiểu abđược biết, thì chi phí bộ nhớ của các kiểu dữ liệu đại số là bao nhiêu, chẳng hạn như:

data Uno = Uno a
data Due = Due a b

Ví dụ, các giá trị này chiếm bao nhiêu byte trong bộ nhớ?

1 :: Int8
1 :: Integer
2^100 :: Integer
\x -> x + 1
(1 :: Int8, 2 :: Int8)
[1] :: [Int8]
Just (1 :: Int8)
Nothing

Tôi hiểu rằng phân bổ bộ nhớ thực tế cao hơn do thu thập rác bị trì hoãn. Nó có thể khác biệt đáng kể do lười đánh giá (và kích thước thunk không liên quan đến kích thước của giá trị). Câu hỏi đặt ra là, với một kiểu dữ liệu, giá trị của nó chiếm bao nhiêu bộ nhớ khi được đánh giá đầy đủ?

Tôi thấy có một :set +stùy chọn trong GHCi để xem thống kê bộ nhớ, nhưng không rõ làm thế nào để ước tính lượng bộ nhớ của một giá trị.

Câu trả lời:


156

(Điều sau áp dụng cho GHC, các trình biên dịch khác có thể sử dụng các quy ước lưu trữ khác nhau)

Quy tắc chung: một hàm tạo tốn một từ cho tiêu đề và một từ cho mỗi trường . Ngoại lệ: một phương thức khởi tạo không có trường (như Nothinghoặc True) sẽ không chiếm không gian, bởi vì GHC tạo một phiên bản duy nhất của các hàm tạo này và chia sẻ nó giữa tất cả các lần sử dụng.

Một từ là 4 byte trên máy 32 bit và 8 byte trên máy 64 bit.

Vậy vd

data Uno = Uno a
data Due = Due a b

an Unocó 2 từ và a Duechiếm 3.

Các Intloại được định nghĩa là

data Int = I# Int#

bây giờ, Int#có một từ, vì vậy Intcó tổng cộng 2. Hầu hết các loại không có hộp bọc lấy một lời, ngoại trừ Int64#, Word64#Double#(trên một máy 32-bit) mà mất 2. GHC thực sự có một bộ nhớ cache của các giá trị nhỏ kiểu IntChar, vì vậy trong nhiều trường hợp, những mất không gian đống ở tất cả. A Stringchỉ yêu cầu không gian cho các ô danh sách, trừ khi bạn sử dụng Chars> 255.

An Int8có đại diện giống hệt với Int. Integerđược định nghĩa như thế này:

data Integer
  = S# Int#                            -- small integers
  | J# Int# ByteArray#                 -- large integers

vì vậy một số nhỏ Integer( S#) có 2 từ, nhưng một số nguyên lớn chiếm một lượng không gian thay đổi tùy thuộc vào giá trị của nó. A ByteArray#nhận 2 từ (tiêu đề + kích thước) cộng với khoảng trắng cho chính mảng.

Lưu ý rằng một hàm tạo được xác định với newtypelà miễn phí . newtypehoàn toàn là một ý tưởng thời gian biên dịch, và nó không tốn dung lượng và không tốn phí hướng dẫn tại thời gian chạy.

Thêm chi tiết trong Bố cục của các đối tượng đống trong Bình luận GHC .


1
Cảm ơn, Simon. Đây chính xác là những gì tôi muốn biết.
sastanin

2
Tiêu đề không phải là hai từ? Một cho thẻ và một cho con trỏ chuyển tiếp để sử dụng trong GC hoặc đánh giá? Vì vậy, sẽ không thêm một từ vào tổng số của bạn?
Edward KMETT

5
@Edward: Thunks bị ghi đè bởi indirections (sau này được GC loại bỏ), nhưng đó chỉ là 2 từ và mọi đối tượng heap được đảm bảo có kích thước ít nhất là 2 2 từ. Nếu không có bất kỳ tính năng lập hồ sơ hoặc gỡ lỗi nào được bật, tiêu đề thực sự chỉ là một từ. Trong GHC, nghĩa là, các triển khai khác có thể làm những việc khác.
nominolo

3
nominolo: có, nhưng từ Closure.h: / * Một cú đánh có một từ đệm để lấy giá trị cập nhật. Điều này là để bản cập nhật không ghi đè tải trọng, vì vậy chúng tôi có thể tránh cần phải khóa thunk trong quá trình nhập và cập nhật. Lưu ý: điều này không áp dụng cho THUNK_STATICs không có trọng tải. Lưu ý: chúng tôi để lại từ đệm này theo mọi cách, thay vì chỉ SMP, để chúng tôi không phải biên dịch lại tất cả các thư viện của mình cho SMP. * / Tải trọng không bị ghi đè trong quá trình chuyển hướng. Hướng dẫn được viết ở một vị trí riêng biệt trong Header.
Edward KMETT

6
Có, nhưng lưu ý điều này chỉ dành cho côn đồ . Nó không áp dụng cho các hàm tạo. Ước tính kích thước của một cú đánh dù sao cũng hơi khó - bạn phải đếm các biến tự do.
nominolo

4

Gói ghc-datasize cung cấp hàm recursiveSize để tính kích thước của đối tượng GHC. Tuy nhiên...

Việc thu gom rác được thực hiện trước khi tính toán kích thước, vì bộ thu gom rác sẽ làm cho việc đi lại trong đống trở nên khó khăn.

... vì vậy sẽ không thực tế nếu gọi điều này thường xuyên!

Cũng xem Làm thế nào để tìm ra các biểu diễn bộ nhớ của GHC của các kiểu dữ liệu? Làm cách nào để xác định kích thước của một loại trong Haskell? .

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.