5

我正在编写一个与大型(10-1000 GB)内存映射二进制文件交互的应用程序,该文件基本上包含一堆相互引用的对象。我想出了一种机制来读/写这些数据,它是有效的,但丑陋而冗长(imo)。

问:有没有更优雅的方式来实现我所做的事情?

我有一个用于结构化数据的类型类,其中一种方法可以将结构读入 Haskell 数据类型(DataOp是 a ReaderTaround 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.BinaryByteStrings以正常方式将其写入磁盘。
  • 用于deriving Generic创建通用读写函数
  • 重载函数引用
  • 用一元镜头做一些神奇的事情。

编辑:SSCCE 按要求

4

1 回答 1

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 同构,但它有 Storable 实例。

于 2013-08-07T20:29:11.930 回答