将 Data.ByteString.Lazy 转换为 CStringLen 的最有效方法

Posted

技术标签:

【中文标题】将 Data.ByteString.Lazy 转换为 CStringLen 的最有效方法【英文标题】:Most efficient way of converting a Data.ByteString.Lazy to a CStringLen 【发布时间】:2020-02-15 06:26:37 【问题描述】:

我需要将一些数据编码为 JSON,然后使用 hsyslog 推送到 syslog。两个相关函数的类型分别是:

Aeson.encode :: a -> Data.ByteString.Lazy.ByteString

System.Posix.Syslog.syslog :: Maybe Facility
                           -> Priority
                           -> CStringLen
                           -> IO () 

转换Lazy.ByteString -> CStringLen 的最有效方法(速度和内存)是什么?我找到了Data.ByteString.Unsafe,但它只适用于ByteString,不适用于Lazy.ByteString

我可以贴一个unsafeUseAsCStringLen . Data.String.Conv.toS 并收工吗?效率方面是否正确?

【问题讨论】:

【参考方案1】:

我想我会使用Data.ByteString.Lazy.toStrict 代替toS,以避免额外的包依赖。

无论如何,您找不到比以下更有效的方法:

unsafeUseAsCStringLen (toStrict lbs) $ \cstrlen -> ...

一般来说,toStrict 是一个“昂贵”的操作,因为懒惰的ByteString 通常由一堆“块”组成,每个“块”都包含一个严格的ByteString,而且不一定还加载到内存中。 toStrict 函数必须强制所有严格的ByteString 块进入内存,并确保在应用无复制unsafeUseAsCStringLen 之前将它们复制到严格的ByteString 所需的单个连续块中。

然而, toStrict 处理一个懒惰的ByteString,它最好由一个块组成,无需任何复制。

实际上,aeson 使用高效的Data.ByteString.Builder 创建 JSON,如果 JSON 相当小(我认为小于 4k),它将构建一个单块惰性ByteString。在这种情况下toStrict是零拷贝,unsafeUseAsCStringLen是零拷贝,整个操作基本是免费的。

但请注意,在您的应用程序中,您将字符串传递给系统记录器,担心此操作的效率是疯狂的。我的猜测是,您甚至需要数千次复制操作才能降低整体操作的性能。

【讨论】:

"不一定还加载到内存中。" -- 你为什么这么说?这是否意味着惰性 ByteStrings 在后台进行惰性 IO?如果我调用BSL.readFile,它不会将所有内容加载到内存中吗?如果我调用BSL.readFile,删除文件,然后尝试访问读取的整个惰性字节串会发生什么? 你回答的最后两段解决了我原来的问题。谢谢你有见地的回复。有时间请看能不能解释一下***.com/questions/60236427/… 是的,BSL.readFile 在后台执行惰性 I/O。原则上它可以执行严格的 I/O,但是返回一个懒惰的 BS 就没有多大意义了。尽管如此,如果您使用BSL.readFile,然后删除文件并尝试读取内容,它仍然可以正常工作,但这是因为文件句柄保持打开状态,并且仍然可以从已删除文件的打开句柄中读取数据. (嗯,某些网络文件系统除外。)但是,如果您破坏性地修改文件(覆盖或截断它),那么这些更改通常会反映在延迟读取的值中。

以上是关于将 Data.ByteString.Lazy 转换为 CStringLen 的最有效方法的主要内容,如果未能解决你的问题,请参考以下文章

Haskell语言学习笔记(49)ByteString Text

怎么用ABBYY将PDF转换为JPEG图像

将 A 转换为 1 B 转换为 2 ... Z 转换为 26,然后将 AA 转换为 27 AB 转换为 28(Excel 中列引用的列索引)

手机如何将照片转换为pdf

如何将CSV格式转换成JSON格式

如何将CString转换成wstring