是否有任何希望将ForeignPtr转换为ByteArray#(对于函数:: ByteString - > Vector)

时间:2011-02-05 18:52:47

标签: haskell ghc

出于性能原因,我希望将ByteString(严格的,现在为)的零拷贝强制转换为Vector。由于Vector只是一个ByteArray#,而ByteStringForeignPtr,这可能类似于:

caseBStoVector :: ByteString -> Vector a
caseBStoVector (BS fptr off len) =
    withForeignPtr fptr $ \ptr -> do
        let ptr' = plusPtr ptr off
            p = alignPtr ptr' (alignment (undefined :: a))
            barr = ptrToByteArray# p len  -- I want this function, or something similar 
            barr' = ByteArray barr
            alignI = minusPtr p ptr
            size = (len-alignI) `div` sizeOf (undefined :: a)
        return (Vector 0 size barr')

这当然不对。即使缺少函数ptrToByteArray#,这似乎也需要逃离ptr范围之外的withForeignPtr。所以我的想法是:

  1. 这篇文章可能会宣传我对ByteArray#的原始理解,如果有人可以谈谈ByteArray#,它的表现,如何管理(GCed)等我会感激不尽

  2. ByteArray#生活在GCed堆上而ForeignPtr是外部的这一事实似乎是一个基本问题 - 所有访问操作都不同。也许我应该考虑从Vector重新定义= ByteArray !Int !Int到另一个间接的东西?像= Location !Int !Int那样data Location = LocBA ByteArray | LocFPtr ForeignPtr,并为这两种类型提供包装操作?这种间接可能会对性能造成太大影响。

  3. 未能将这两者结合在一起,也许我可以更有效地访问ForeignPtr中的任意元素类型。有没有人知道将ForeignPtr(或ByteString)视为任意StorablePrimitive类型数组的库?这仍然会让我失去流融合并从Vector包中调整。

2 个答案:

答案 0 :(得分:8)

免责声明:此处的所有内容均为实施细节,具体针对GHC以及发布时相关图书馆的内部表示。

这个响应是在事实发生后的几年,但确实可以获得指向bytearray内容的指针。这是有问题的,因为GC喜欢在堆中移动数据,而且GC堆之外的东西可能会泄漏,这不一定是理想的。 GHC通过以下方式解决了这个问题:

newPinnedByteArray# :: Int# -> State# s -> (#State# s, MutableByteArray# s#)

原始字节数组(内部typedef'd C char数组)可以静态固定到一个地址。 GC保证不移动它们。您可以使用此函数将bytearray引用转换为指针:

byteArrayContents# :: ByteArray# -> Addr#

地址类型构成了Ptr和ForeignPtr类型的基础。 Ptrs是用虚线类型标记的地址,ForeignPtrs是GHC内存和IORef终结器的可选引用。

免责声明:如果您的ByteString是Haskell构建的,那么只会工作。否则,您无法获得对bytearray的引用。您不能取消引用任意地址。不要试图强制转换或强制转换为bytearray;那种方式就是段错误。例如:

{-# LANGUAGE MagicHash, UnboxedTuples #-}

import GHC.IO
import GHC.Prim
import GHC.Types

main :: IO()
main = test

test :: IO ()        -- Create the test array.
test = IO $ \s0 -> case newPinnedByteArray# 8# s0 of {(# s1, mbarr# #) ->
                     -- Write something and read it back as baseline.
                   case writeInt64Array# mbarr# 0# 1# s1 of {s2 ->
                   case readInt64Array# mbarr# 0# s2 of {(# s3, x# #) ->
                     -- Print it. Should match what was written.
                   case unIO (print (I# x#)) s3 of {(# s4, _ #) ->
                     -- Convert bytearray to pointer.
                   case byteArrayContents# (unsafeCoerce# mbarr#) of {addr# ->
                     -- Dereference the pointer.
                   case readInt64OffAddr# addr# 0# s4 of {(# s5, x'# #) ->
                     -- Print what's read. Should match the above.
                   case unIO (print (I# x'#)) s5 of {(# s6, _ #) ->
                     -- Coerce the pointer into an array and try to read.
                   case readInt64Array# (unsafeCoerce# addr#) 0# s6 of {(# s7, y# #) ->
                     -- Haskell is not C. Arrays are not pointers.
                     -- This won't match. It might segfault. At best, it's garbage.
                   case unIO (print (I# y#)) s7 of (# s8, _ #) -> (# s8, () #)}}}}}}}}


Output:
   1
   1
 (some garbage value)

要从ByteString获取bytearray,您需要从Data.ByteString.Internal和模式匹配中导入构造函数。

data ByteString = PS !(ForeignPtr Word8) !Int !Int
(\(PS foreignPointer offset length) -> foreignPointer)

现在我们需要将商品从ForeignPtr中撕掉。这部分完全是针对特定于实现的。对于GHC,从GHC.ForeignPtr。

导入
data ForeignPtr a = ForeignPtr Addr# ForeignPtrContents
(\(ForeignPtr addr# foreignPointerContents) -> foreignPointerContents)

data ForeignPtrContents = PlainForeignPtr !(IORef (Finalizers, [IO ()]))
                        | MallocPtr      (MutableByteArray# RealWorld) !(IORef (Finalizers, [IO ()]))
                        | PlainPtr       (MutableByteArray# RealWorld)

在GHC中,ByteString是使用PlainPtrs构建的,它们固定在固定字节数组中。他们没有终结者。当它们超出范围时,它们就像常规的Haskell数据一样。不过,地址不算数。 GHC假设他们指向GC堆之外的东西。如果bytearray本身超出了范围,你就会留下一个悬空指针。

data PlainPtr = (MutableByteArray# RealWorld)
(\(PlainPtr mutableByteArray#) -> mutableByteArray#)

MutableByteArrays与ByteArrays相同。如果您想要真正的零拷贝构造,请确保将unsafeCoerce#或unsafeFreeze#设置为bytearray。否则,GHC会创建副本。

mbarrTobarr :: MutableByteArray# s -> ByteArray#
mbarrTobarr = unsafeCoerce#

现在你已经准备好将ByteString的原始内容变成一个向量。

祝福,

答案 1 :(得分:2)

你可能能够将某些东西:: ForeignPtr -> Maybe ByteArray#混在一起,但一般来说你无能为力。

您应该查看Data.Vector.Storable模块。它包含一个函数unsafeFromForeignPtr :: ForeignPtr a -> Int -> Int -> Vector a。这听起来像你想要的。

还有一个Data.Vector.Storable.Mutable变体。