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