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

Haskell 为什么hGetBuf、hPutBuf等要分配内存?,haskell,io,profiling,Haskell,Io,Profiling,在做一些简单的基准测试的过程中,我遇到了一些让我吃惊的事情。请从以下内容获取此代码段: hSplice::Int->Handle->Handle->IO() hSplice len s t=do 一个IO() echo buf=永远$do len我会根据答案来猜 运行时试图优化小的读写操作,所以它维护内部缓冲区。如果缓冲区长度为1字节,则直接使用它将效率低下。所以内部缓冲区被用来读取更大的数据块。它可能约32Kb长。再加上类似的写作技巧。加上你自己的缓冲区 代码有一个优化——如果您提供的缓冲区比

在做一些简单的基准测试的过程中,我遇到了一些让我吃惊的事情。请从以下内容获取此代码段:

hSplice::Int->Handle->Handle->IO()
hSplice len s t=do
一个IO()
echo buf=永远$do

len我会根据答案来猜

运行时试图优化小的读写操作,所以它维护内部缓冲区。如果缓冲区长度为1字节,则直接使用它将效率低下。所以内部缓冲区被用来读取更大的数据块。它可能约32Kb长。再加上类似的写作技巧。加上你自己的缓冲区

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

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

添加:

这个似乎是分配的,但我仍然不知道为什么

您关心的是什么,最大内存使用量还是分配的字节数

c\u read
是一个c函数,它不在haskell的堆上分配(但可以在c堆上分配)


readRawBufferPtr
是Haskell函数,Haskell函数通常会分配大量内存,这很快就会变成垃圾。仅仅是因为不变性。haskell程序通常在内存使用量低于1Mb时分配100Gb的内存。

结论似乎是:。

你不是在分配内存来创建
字节吗?@GabrielGonzalez我不这么认为。。。当我使用
+RTS-hy
评测时,堆上的主要类型是
ARR_WORDS
字节的类型应为
Int
(读取的字节数)。@mergeconflict表示ARR_字对应于ByteArray。我不知道为什么要分配这么多,但配置文件中的平稳状态表明程序在恒定空间中运行。@cheecheeo是的,这是正确的。我没有空间泄漏;相反,每次循环中似乎都会分配和释放一个或多个小型缓冲区。您是否分析过执行其他操作(可能是无操作)的代码,并验证您是否有不同的行为?我关心的是分配和GC的CPU成本,而不是最大内存使用量。例如:深入研究
readRawBufferPtr
的实现,似乎在调用
throwErrnoIfMinus1RetryMayBlock
时发生了堆分配;如果我在一个紧密的内部循环中使用它,我希望这个调用花费尽可能少的周期。我不认为这是由于不变性/纯函数风格导致的分配示例。@mergeconflict:看起来分配是由于使用了高阶函数(
ThrowernOifminus1RetryMayblock
,如您所述),而这些函数没有得到优化。然而,如果您关心分配的CPU成本,那么您应该关心GHC输出的所有内容的CPU成本,而不是仅仅因为分配易于测量而关注它。分配是两条指令:递增指针并根据堆限制对其进行测试。许多GHC的输出比这更糟糕,与执行系统调用(
read
)相比,这些输出应该可以忽略不计。那么,问题是性能?您可能应该在问题正文和标题中反映这一点。在您的案例中,为什么您认为内存分配与性能有关?这可能是一个瓶颈,但这需要论证。这不是一个真正的bug,只是错过了一个优化的机会。
                                        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
echo :: Ptr Word8 -> IO ()
echo buf = forever $ do
  threadWaitRead $ Fd 0
  len <- c_read 0 buf 1
  c_write 1 buf (fromIntegral len)
  yield
echo :: Ptr Word8 -> IO ()
echo buf = forever $ do
  len <- readRawBufferPtr "read" stdin buf 0 1
  writeRawBufferPtr "write" stdout buf 0 (fromIntegral len)