gpt4 book ai didi

haskell - hGetBuf、hPutBuf等为什么要分配内存?

转载 作者:行者123 更新时间:2023-12-03 22:45:09 27 4
gpt4 key购买 nike

在做一些简单的基准测试的过程中,我遇到了一些令我惊讶的事情。从 Network.Socket.Splice 获取此片段:

hSplice :: Int -> Handle -> Handle -> IO ()
hSplice len s t = do
a <- mallocBytes len :: IO (Ptr Word8)
finally
(forever $! do
bytes <- hGetBufSome s a len
if bytes > 0
then hPutBuf t a bytes
else throwRecv0)
(free a)

人们会期望 hGetBufSomehPutBuf这里不需要分配内存,因为它们写入和读取预先分配的缓冲区。 docs似乎支持这种直觉......但唉:
                                        individual     inherited
COST CENTRE %time %alloc %time %alloc bytes

hSplice 0.5 0.0 38.1 61.1 3792
hPutBuf 0.4 1.0 19.8 29.9 12800000
hPutBuf' 0.4 0.4 19.4 28.9 4800000
wantWritableHandle 0.1 0.1 19.0 28.5 1600000
wantWritableHandle' 0.0 0.0 18.9 28.4 0
withHandle_' 0.0 0.1 18.9 28.4 1600000
withHandle' 1.0 3.8 18.8 28.3 48800000
do_operation 1.1 3.4 17.8 24.5 44000000
withHandle_'.\ 0.3 1.1 16.7 21.0 14400000
checkWritableHandle 0.1 0.2 16.4 19.9 3200000
hPutBuf'.\ 1.1 3.3 16.3 19.7 42400000
flushWriteBuffer 0.7 1.4 12.1 6.2 17600000
flushByteWriteBuffer 11.3 4.8 11.3 4.8 61600000
bufWrite 1.7 6.9 3.0 9.9 88000000
copyToRawBuffer 0.1 0.2 1.2 2.8 3200000
withRawBuffer 0.3 0.8 1.2 2.6 10400000
copyToRawBuffer.\ 0.9 1.7 0.9 1.7 22400000
debugIO 0.1 0.2 0.1 0.2 3200000
debugIO 0.1 0.2 0.1 0.2 3200016
hGetBufSome 0.0 0.0 17.7 31.2 80
wantReadableHandle_ 0.0 0.0 17.7 31.2 32
wantReadableHandle' 0.0 0.0 17.7 31.2 0
withHandle_' 0.0 0.0 17.7 31.2 32
withHandle' 1.6 2.4 17.7 31.2 30400976
do_operation 0.4 2.4 16.1 28.8 30400880
withHandle_'.\ 0.5 1.1 15.8 26.4 14400288
checkReadableHandle 0.1 0.4 15.3 25.3 4800096
hGetBufSome.\ 8.7 14.8 15.2 24.9 190153648
bufReadNBNonEmpty 2.6 4.4 6.1 8.0 56800000
bufReadNBNonEmpty.buf' 0.0 0.4 0.0 0.4 5600000
bufReadNBNonEmpty.so_far' 0.2 0.1 0.2 0.1 1600000
bufReadNBNonEmpty.remaining 0.2 0.1 0.2 0.1 1600000
copyFromRawBuffer 0.1 0.2 2.9 2.8 3200000
withRawBuffer 1.0 0.8 2.8 2.6 10400000
copyFromRawBuffer.\ 1.8 1.7 1.8 1.7 22400000
bufReadNBNonEmpty.avail 0.2 0.1 0.2 0.1 1600000
flushCharReadBuffer 0.3 2.1 0.3 2.1 26400528

heap profile by module
heap profile by type

我不得不假设这是故意的……但我不知道那个目的可能是什么。更糟糕的是:我只是勉强够聪明地得到这个配置文件,但还不够聪明,无法准确地弄清楚分配了什么。

任何有关这些方面的帮助将不胜感激。

更新:我用两个大大简化的测试用例做了更多的分析。第一个测试用例直接使用 System.Posix.Internals 中的读/写操作:
echo :: Ptr Word8 -> IO ()
echo buf = forever $ do
threadWaitRead $ Fd 0
len <- c_read 0 buf 1
c_write 1 buf (fromIntegral len)
yield

正如您所希望的,每次循环都不会在堆上分配内存。第二个测试用例使用 GHC.IO.FD 中的读/写操作。 :
echo :: Ptr Word8 -> IO ()
echo buf = forever $ do
len <- readRawBufferPtr "read" stdin buf 0 1
writeRawBufferPtr "write" stdout buf 0 (fromIntegral len)

更新#2:有人建议我将其作为 GHC Trac 中的错误提交...我仍然不确定它是否真的是错误(与故意行为、已知限制或其他相反)但这里是: https://ghc.haskell.org/trac/ghc/ticket/9696

最佳答案

我会尝试根据 code 进行猜测

运行时尝试优化小型读写,因此它维护内部缓冲区。如果你的缓冲区是 1 字节长,那么直接使用它是低效的。因此内部缓冲区用于读取更大的数据 block 。它可能长约 32Kb。加上类似的写作。加上你自己的缓冲区。

代码有一个优化——如果你提供的缓冲区比内部缓冲区大,而后者是空的,它将直接使用你的缓冲区。但是内部缓冲区已经分配了,所以它不会减少内存使用。我不知道如何禁用内部缓冲区,但如果它对您很重要,您可以打开功能请求。

(我意识到我的猜测可能完全错误。)

地址:

This one does seem to allocate, but I still don't know why.



您关心什么,最大内存使用量或分配的字节数?
c_read是一个 C 函数,它不在 haskell 的堆上分配(但可能在 C 堆上分配。)
readRawBufferPtr是 Haskell 函数,haskell 函数通常会分配大量内存,这很快就会变成垃圾。仅仅因为不变性。 haskell 程序在内存使用量低于 1Mb 时分配例如 100Gb 是很常见的。

关于haskell - hGetBuf、hPutBuf等为什么要分配内存?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26333815/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com