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

2023-11-24

出于性能原因,我想要一个零拷贝的转换ByteString(目前严格)Vector。自从Vector只是一个ByteArray#在引擎盖下,以及ByteString is a 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')

这当然是不对的。即使缺少功能ptrToByteArray#这似乎需要逃避ptr之外的withForeignPtr范围。所以我的问题是:

  1. 这篇文章可能宣扬了我对ByteArray#,如果有人可以谈谈ByteArray#,它的代表性,它是如何管理的(GCed)等等,我将不胜感激。

  2. 事实是ByteArray#存在于 GCed 堆上并且ForeignPtr是外部的似乎是一个基本问题——所有的访问操作都是不同的。也许我应该考虑重新定义Vector from = ByteArray !Int !Int到另一个间接的东西?就像是= Location !Int !Int where data Location = LocBA ByteArray | LocFPtr ForeignPtr并为这两种类型提供包装操作?不过,这种间接方式可能会严重损害性能。

  3. 如果无法将这两者结合在一起,也许我可以访问 a 中的任意元素类型ForeignPtr以更有效的方式。有谁知道有一个图书馆可以处理ForeignPtr (or ByteString) 作为任意数组Storable or Primitive类型?这仍然会让我失去 Vector 包中的流融合和调整。


免责声明:这里的所有内容都是实施细节,特定于 GHC 以及发布时相关库的内部表示。

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

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

原始字节数组(内部类型定义的 C 字符数组)可以静态固定到地址。 GC 保证不移动它们。您可以使用以下函数将字节数组引用转换为指针:

byteArrayContents# :: ByteArray# -> Addr#

地址类型构成了 Ptr 和foreignPtr 类型的基础。 Ptrs 是用幻像类型标记的地址,ForeignPtrs 是加上对 GHC 内存和 IORef 终结器的可选引用。

免责声明:这将only如果你的 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 导入构造函数并进行模式匹配。

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 是使用 PlainPtr 构建的,这些 PlainPtr 包裹在固定字节数组周围。它们没有终结器。当它们超出范围时,它们会像常规 Haskell 数据一样被 GC 处理。不过,地址不算数。 GHC 假设它们指向 GC 堆之外的东西。如果字节数组本身超出了范围,那么您将留下一个悬空指针。

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

MutableByteArray 与 ByteArray 相同。如果您想要真正的零拷贝构造,请确保对字节数组使用 unsafeCoerce# 或 unsafeFreeze# 。否则,GHC 会创建一个副本。

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

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

最好的祝愿,

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

是否有希望将ForeignPtr转换为ByteArray#(对于函数::ByteString -> Vector) 的相关文章

  • 如何在Haskell中实现词法分析器和解析器

    我在这里得到了这段代码 它是用Haskell结构的命令式编程语言编写的程序 所以问题是 我如何为这种语言实现词法分析器和解析器 该程序被定义为一系列语句有 6 种类型 goto write stop if goto 和 int int n
  • 类型级别集结合律的证明

    我试图证明类型级函数Union https hackage haskell org package type level sets 0 8 5 0 docs Data Type Set html t Union是关联的 但我不确定应该如何完
  • 迭代打印列表中的每个整数

    假设我有一个整数列表l 1 2 我想打印到stdout Doing print l产生 1 2 假设我想打印不带大括号的列表 map print l产生 No instance for Show IO arising from a use
  • 为什么在 where 子句中使用类型签名如此罕见?

    它是否有助于编译器优化 或者只是添加额外类型签名的多余工作 例如 人们经常看到 foo a gt b foo x bar x where bar x undefined 而不是 foo a gt b foo x bar x where ba
  • Haskell:需要了解 Functor 的签名

    有人能给我解释一下 Functor 的签名吗 Prelude gt info Functor class Functor f gt where fmap a gt b gt f a gt f b lt a gt f b gt f a 我不明
  • Haskell 处理负参数

    尝试对两个值求和 其中只有一个为负值 例如 1 and 2 soma Float gt Float gt Float soma x1 x2 x1 x2 结果出现错误 为什么
  • Haskell:对 Num 类型类的使用感到困惑

    我很困惑为什么这有效 f Num a gt a gt a f x x 42 但这并没有 g Num a gt a gt a g x x 4 2 我本来就明白Num包含实现运算符的所有类型 因此 如果42 is an Int and 4 2
  • 在 ghci 下执行 `(read "[Red]") :: [Color]` 时会发生什么?

    我正在阅读以下小节现实世界 Haskell 第 6 章 类型类 http book realworldhaskell org read using typeclasses html关于一个实例Read for Color 它实现了reads
  • 如何在 Haskell Pipes 中将两个 Consumer 合并为一个?

    我使用Haskell流处理库pipes https hackage haskell org package pipes编写一个命令行工具 每个命令行操作都可以将结果输出到stdout并记录到stderr with pipes API I n
  • 为什么 mod 在表达式中给出的结果与在函数调用中给出的结果不同?

    假设有人想要计算函数 f x y x mod 3 y mod 3 mod 2 那么 如果再展开f 1 0 手动 可以得到 1 mod 3 0 mod 3 mod 2 1 然而 如果使用内联函数 结果是 let f x y x mod 3 y
  • 如何让 do 块提前返回?

    我正在尝试使用 Haskell 抓取网页并将结果编译到一个对象中 如果出于某种原因 我无法从页面获取所有项目 我想停止尝试处理页面并提前返回 例如 scrapePage String gt IO scrapePage url do doc
  • 由于垃圾收集,Haskell 程序中会出现多长时间的暂停?

    关于我的另一个问题Haskell 集合可以保证每个操作的最坏情况范围 https stackoverflow com q 12393104 1333025 我很好奇 垃圾收集会导致多长时间的暂停 Haskell 是否使用某种增量垃圾收集 以
  • Haskell / cabal 包的解决方法受到 Nix 和 Cabal 的限制?

    我最近开始开发反射平台 https github com reflex frp reflex platform 有一些额外的配置类似于优秀的反射项目骨架 https github com ElvishJerricco reflex proj
  • Python 比编译的 Haskell 更快?

    我有一个用 Python 和 Haskell 编写的简单脚本 它读取包含 1 000 000 个换行符分隔的整数的文件 将该文件解析为整数列表 对其进行快速排序 然后将其写入已排序的不同文件中 该文件与未排序的文件具有相同的格式 简单的 这
  • 在 Archlinux 上使用 Vim 作为 Haskell 的 IDE 目前情况如何?

    如果可行的话 我的目标是通过 YouCompleteMe 在 Vim 中完成 Haskell 的命令 在这方面 正如您在下面看到的 我还没有找到关于如何让它发挥作用的共识 相关评论的最新评论YouCompleteMe 上的问题 https
  • 在ghci中,如何删除现有的绑定?

    我收到一个 绑定影响现有绑定 错误 类似于以下错误this https stackoverflow com questions 2902716 in haskell what does it mean if a binding shadow
  • 不同类型的列表?

    data Plane Plane point Point normal Vector Double data Sphere Sphere center Point radius Double class Shape s where inte
  • Haskell/Idris 中的开放类型级别证明

    在 Idris Haskell 中 可以通过注释类型并使用 GADT 构造函数 例如使用 Vect 来证明数据的属性 但这需要将属性硬编码到类型中 例如 Vect 必须是与 List 不同的类型 是否有可能拥有具有开放属性集的类型 例如同时
  • Haskell if-then-else 条件中的“解析输入错误”

    当我尝试编译以下 do 块时 它会抛出错误 输入 conn 上的解析错误 我尝试了许多不同的 if then else 语句配置 但均无济于事 在我添加条件之前 数据库逻辑就起作用了 所以这没有问题 else 中是否有太多行 有没有办法在不
  • seq在haskell中代表什么

    我是 Haskell 的新手 刚刚进入惰性世界编程 我读到seq函数非常特殊 因为它强制使用严格的评估 以便在某些情况下更加有效 但我就是找不到什么seq代表字面意思 也许严格评估Q 它应该提醒您 顺序 或 顺序 因为它允许程序员指定其参数

随机推荐