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

Tho*_*son 10 haskell ghc

出于性能原因,我希望将ByteString(严格的,现在的)零拷贝演绎到a 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')
Run Code Online (Sandbox Code Playgroud)

这当然是不对的.即使缺少功能,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包中调整.

use*_*093 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)
Run Code Online (Sandbox Code Playgroud)

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

data ByteString = PS !(ForeignPtr Word8) !Int !Int
(\(PS foreignPointer offset length) -> foreignPointer)
Run Code Online (Sandbox Code Playgroud)

现在我们需要将商品从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)
Run Code Online (Sandbox Code Playgroud)

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

data PlainPtr = (MutableByteArray# RealWorld)
(\(PlainPtr mutableByteArray#) -> mutableByteArray#)
Run Code Online (Sandbox Code Playgroud)

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

mbarrTobarr :: MutableByteArray# s -> ByteArray#
mbarrTobarr = unsafeCoerce#
Run Code Online (Sandbox Code Playgroud)

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

最好的祝愿,