出于性能原因,我希望将ByteString
(严格的,现在的)零拷贝演绎到a Vector
.由于Vector
只是ByteArray#
在引擎盖下,并ByteString
是ForeignPtr
这可能看起来是这样的:
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
.所以我的想法是:
这篇文章可能会宣传我的原始理解ByteArray#
,如果有人可以谈一谈ByteArray#
,它的表现形式,如何管理(GCed)等我会很感激.
ByteArray#
生活在GCed堆上并且ForeignPtr
是外部的这一事实似乎是一个基本问题 - 所有访问操作都是不同的.也许我应该看看重新定义Vector
的= ByteArray !Int !Int
东西与其他间接?喜欢= Location !Int !Int
哪里data Location = LocBA ByteArray | LocFPtr ForeignPtr
并为这两种类型提供包装操作?这种间接可能会对性能造成太大影响.
如果不将这两者结合在一起,也许我可以ForeignPtr
以更有效的方式访问任意元素类型.有没有人知道一个库ForeignPtr
(或ByteString
)作为一个任意Storable
或Primitive
类型的数组?这仍然会让我失去流融合和从Vector包中调整.
免责声明:此处的所有内容均为实施细节,具体针对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的原始内容变成一个向量.
最好的祝愿,