Tôi đang viết một ứng dụng tương tác với tệp nhị phân được ánh xạ bộ nhớ lớn (10-1000 GB), giữ cơ bản một loạt các đối tượng tham chiếu với nhau. Tôi đã đưa ra một cơ chế để đọc/ghi dữ liệu này có hiệu quả, nhưng xấu xí và tiết (imo).Lưu trữ dữ liệu nhị phân có cấu trúc lớn với Haskell
Hỏi: Có cách nào thanh lịch hơn để đạt được những gì tôi đã làm không?
Tôi có một kiểu chữ cho dữ liệu có cấu trúc, với một phương pháp đọc cấu trúc thành kiểu dữ liệu Haskell (DataOp
là ReaderT
xung quanh IO
).
class DBStruct a where
structRead :: Addr a -> DataOp a
Để làm điều này dễ đọc hơn, tôi có một typeclass xác định những gì các thành viên cấu trúc đi nơi:
class DBStruct st => StructMem structTy valTy name | structTy name -> valTy where
offset :: structTy -> valTy -> name -> Int64
Tôi có một vài chức năng helper mà sử dụng phương pháp bù đắp cho các yếu tố cấu trúc đọc/ghi, để đọc cấu trúc từ các tham chiếu được lưu trữ và để đọc chậm trễ cấu trúc đọc (để cho phép đọc toàn bộ tệp một cách lười biếng).
Vấn đề với điều này là nó liên quan đến rất nhiều sự lặp lại để sử dụng. Đối với một cấu trúc, đầu tiên tôi phải xác định kiểu Haskell:
data RowBlock = RowBlock {rbNext :: Maybe RowBlock
,rbPrev :: Maybe RowBlock
,rbRows :: [RowTy]
}
Sau đó name
loại:
data Next = Next
data Prev = Prev
data Count = Count
newtype Row = Row Int64
Sau đó, các trường hợp cho mỗi thành viên cấu trúc:
instance StructMem RowBlock (Maybe (Addr RowBlock)) Next where offset _ _ _ = 0
instance StructMem RowBlock (Maybe (Addr RowBlock)) Prev where offset _ _ _ = 8
instance StructMem RowBlock Int64 Count where offset _ _ _ = 16
instance StructMem RowBlock RowTy Row where offset _ _ (Row n) = 24 + n * 8
Sau đó, cấu trúc phương thức đọc:
instance DBStruct RowBlock where
structRead a = do
n <- elemMaybePtr a Next
p <- elemMaybePtr a Prev
c <- elemRead a Count
rs <- mapM (elemRead a . Row) [0 .. c-1]
return $ RowBlock n p rs
Vì vậy, tất cả những gì tôi thực sự đạt được là triển khai lại các cấu trúc C theo một cách tiết kiệm hơn (và chậm). Tôi sẽ hạnh phúc hơn nếu điều này ngắn gọn hơn trong khi vẫn giữ được sự an toàn kiểu. Chắc chắn đây là một vấn đề thường gặp phải.
Một vài lựa chọn thay thế có thể tôi có thể nghĩ đến là:
- Mương tập tin bộ nhớ ánh xạ và sử dụng
Data.Binary
, viếtByteStrings
vào đĩa theo cách thông thường. - Sử dụng
deriving Generic
để tạo generic đọc và viết các chức năng - Overload Functional References
- Do something huyền diệu với ống kính monadic.
EDIT: SSCCE as requested
Bạn có thể cung cấp một ví dụ đơn giản, khép kín có thể được biên soạn, một thứ gì đó để bắt đầu không? –
@ PetrPudlák Tôi có thể làm điều đó sau ngày hôm nay nếu bạn nghĩ rằng nó sẽ giúp mọi người tìm ra câu trả lời. Tuy nhiên, điều này có nghĩa là có nhiều câu hỏi về kiến trúc hơn là một câu hỏi tại sao không phải là mã của tôi; mã tôi đã đăng có nghĩa là minh họa hơn kiến trúc hiện tại của tôi hơn bất kỳ thứ gì khác. – Dan
Có, [SSCCE] (http://www.sscce.org/) sẽ giúp tôi hiểu rõ hơn về thiết kế hiện tại của bạn. –