出于性能方面的原因,我希望将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范围之外。所以我的问题是:
  • 这篇文章可能宣传了我对ByteArray#的原始理解,如果任何人都可以谈论ByteArray#,它的表示形式,如何管理(GCed)等等,我将不胜感激。
  • ByteArray#驻留在GCed堆上,而ForeignPtr是外部的,这似乎是一个基本问题-所有访问操作都不同。也许我应该看看用另一种间接方法将Vector= ByteArray !Int !Int重新定义为某种东西?像= Location !Int !Int这样的地方,其中data Location = LocBA ByteArray | LocFPtr ForeignPtr可以为这两种类型提供包装操作吗?但是,这种间接方式可能会严重损害性能。
  • 如果无法将这两个结合在一起,也许我可以以更有效的方式访问ForeignPtr中的任意元素类型。有谁知道将ForeignPtr(或ByteString)视为任意StorablePrimitive类型的数组的库?这仍然会让我失去Vector包中的流融合和调整。
  • 最佳答案

    免责声明:这里的所有内容都是实现细节,特定于GHC和发布时相关图书馆的内部表示。

    这个响应是事实发生后的几年,但是确实有可能获得一个指向字节数组内容的指针。这是有问题的,因为GC喜欢在堆中移动数据,并且GC堆之外的内容可能会泄漏,这不一定是理想的。 GHC通过以下方式解决了这一问题:
    newPinnedByteArray# :: Int# -> State# s -> (#State# s, MutableByteArray# s#)
    原始字节数组(内部类型定义的C char数组)可以静态固定到地址。 GC保证不移动它们。您可以使用以下函数将字节数组引用转换为指针:
    byteArrayContents# :: ByteArray# -> Addr#
    地址类型构成Ptr和ForeignPtr类型的基础。 Ptr是标有幻像类型的地址,ForeignPtr是该地址加上对GHC内存和IORef终结器的可选引用。

    免责声明:仅当您的ByteString是Haskell构建的时,此方法才有效。否则,您将无法获得对字​​节数组的引用。您不能取消引用任意地址。不要试图将自己的方式强制转换为字节数组。那就是段错误。例子:

    {-# 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获取字节数组,您需要从Data.ByteString.Internal和pattern匹配中导入构造函数。
    data ByteString = PS !(ForeignPtr Word8) !Int !Int
    (\(PS foreignPointer offset length) -> foreignPointer)
    

    现在我们需要将 cargo 从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构建的,PlainPtrs包裹在固定的字节数组周围。它们没有终结器。当它们超出范围时,它们就像GC的常规Haskell数据一样。不过,加法器不计算在内。 GHC假定它们指向GC堆之外的内容。如果字节数组本身不在范围之内,那么您将剩下一个悬空的指针。
    data PlainPtr = (MutableByteArray# RealWorld)
    (\(PlainPtr mutableByteArray#) -> mutableByteArray#)
    

    MutableByteArrays与ByteArrays相同。如果想要真正的零副本构造,请确保将unsafeCoerce#或unsafeFreeze#设置为字节数组。否则,GHC将创建一个副本。
    mbarrTobarr :: MutableByteArray# s -> ByteArray#
    mbarrTobarr = unsafeCoerce#
    

    现在,您已经准备好将ByteString的原始内容转换为向量。

    最好的祝愿,

    关于haskell - 有没有希望将ForeignPtr转换为ByteArray#(对于函数:: ByteString-> Vector),我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/4908880/

    10-11 21:55