我正在编写一个与大型(10-1000 GB)内存映射二进制文件交互的应用程序,基本上保存了一堆相互引用的对象。我想出了一种读取/写入这些数据的机制,这种机制既有效又丑陋(imo)。
问:有没有更优雅的方式来实现我的目标?
我有一个结构化数据的类型类,有一个方法可以将结构读入Haskell数据类型(DataOp
是ReaderT
IO
周围。
class DBStruct a where
structRead :: Addr a -> DataOp a
为了使这个更具可读性,我有另一个类型类定义了哪些结构成员去了哪里:
class DBStruct st => StructMem structTy valTy name | structTy name -> valTy where
offset :: structTy -> valTy -> name -> Int64
我有一些辅助函数,它们使用offset方法读取/写入结构元素,从存储的引用读取结构,以及延迟推迟结构读取(允许延迟读取整个文件)。
这个问题是它需要大量重复才能使用。对于一个结构,我首先要定义Haskell类型:
data RowBlock = RowBlock {rbNext :: Maybe RowBlock
,rbPrev :: Maybe RowBlock
,rbRows :: [RowTy]
}
然后name
类型:
data Next = Next
data Prev = Prev
data Count = Count
newtype Row = Row Int64
然后是每个结构成员的实例:
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
然后是结构读取方法:
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
所以我真正完成的是以更加冗长(和缓慢)的方式重新实现C结构。如果在保持类型安全的同时更简洁,我会更高兴。当然这是一个常见的问题。
我能想到的一些可能的选择是:
Data.Binary
,以正常方式将ByteStrings
写入磁盘。deriving Generic
创建通用读写功能答案 0 :(得分:1)
您可以尝试将Data.Binary与您的Ptrs一起使用。
写作:
使用Data.Binary构建ByteString。 ByteString是一个元组(ForeignPtr Word8,Int,Int),它保存数据存储的地址,偏移量和长度。您可以使用Data.ByteString.Internal包来获取toForeignPtr,它将为您解包元组。 Foreign.ForeignPtr提供withForeignPtr,它接受一个通过指针执行IO操作的函数。在那里,你可以将memcpy(在Data.ByteString.Internal中也提供了绑定)将字节串存储到你从mmap获得的mmapped Ptr。
阅读:
您可以使用Data.ByteString.Internal的fromForiegnPtr将Ptr转换为字节字符串。这基本上是mmap库的功能,但你可以一次做一个记录而不是整个区域。在内存上有一个ByteString视图后,可以使用Data.Binary将其解压缩。
另一个选择是利用ByteString在Data.Vector.Storable.ByteString中有一个替代实现的事实,这将允许您使用现在使用的Storable接口将它们读/写到mmaped Ptrs。接口和基本类型与Data.ByteString同构,但它有可存储的实例。