Haskell 堆里满是被钉住的东西

Haskell 堆里满是被钉住的东西,haskell,memory-leaks,Haskell,Memory Leaks,我有一个小程序,有合理的最大居住时间,但分配线性。起初,我认为应该是cons cells或I,但使用-p-hc运行程序表明堆被钉住了。是否有人了解原因和/或可以提出改进建议 节目 -- task27.hs {-# LANGUAGE FlexibleContexts #-} import Control.Monad import Control.Monad.ST import Control.Exception import System.Random import Data.Functor

我有一个小程序,有合理的最大居住时间,但分配线性。起初,我认为应该是cons cells或I,但使用-p-hc运行程序表明堆被钉住了。是否有人了解原因和/或可以提出改进建议

节目

-- task27.hs
{-# LANGUAGE FlexibleContexts #-}
import Control.Monad
import Control.Monad.ST
import Control.Exception 
import System.Random
import Data.Functor

import qualified Data.Vector.Generic.Mutable as V
import qualified Data.Vector.Unboxed as U

m = 120

task27 :: [Int] -> (Int, Int)
task27 l = runST $ do 
    r <- V.replicate m 0 :: ST s (U.MVector s Int)

    let go []     = return (1,2)
        go (a:as) = do
          let p = a `mod` m
          cur_lead <- r `V.read` p
          when (a > cur_lead) (V.write r p a)
          go as
    go l

randTest :: 
  Int -> -- Length of random testing sequence
  IO ()
randTest n =
  newStdGen <&>
  randoms <&> 
  take n <&> 
  task27 >>=
  print

main = randTest 1000000
我的阴谋集团.project.local:

我执行cabal-v0运行task27-+RTS-p-hc和&hp2ps-e8in-c task27.hp并得到以下结果:


我尝试在这里和那里添加刘海,但似乎没有帮助。

正如@WillemVanOnsem所说,用GHC术语来说,35kB的居民是微不足道的。无论您有什么性能问题,它都与这一小块固定内存无关。起初,我说这可能是向量,但那是错误的。文本使用固定内存,但Data.Vector不使用。这段固定内存看起来像是来自运行时系统本身,所以您可以忽略它,如下所示

在GHC代码中,总分配是处理的度量。GHC程序是一个分配引擎。如果它没有分配,它可能没有做任何事情,很少有例外。所以,如果您希望您的算法能够按时运行,那么它也将在总分配中运行,通常相当于千兆字节

对于极少数例外情况,如果积极优化允许使用完全非固定值进行计算,GHC程序可以在恒定总分配但非恒定时间下运行。例如:

main = print (sum [1..10000000] :: Int)
以恒定的总分配(例如,堆上分配的50kB)运行,因为整数可以取消绑定。作为比较

main = print (sum [1..10000000] :: Integer)
在总分配上运行,例如在堆上分配320MB。顺便说一下,试着分析最后一个程序,并增加计数,直到它运行足够长的时间来生成几秒钟的分析数据。您将看到,它使用的固定内存量与您的程序相同,并且该内存量实际上不会随着上限而改变。所以,这只是运行时系统开销

回到你的例子。。。如果您担心性能,罪魁祸首可能是System.Random。这是一个非常慢的随机数生成器。如果我用n=10000000运行你的程序,需要4秒。如果我用简单的LCG替换随机数生成器:

randoms :: Word32 -> [Word32]
randoms seed = tail $ iterate lcg seed
  where lcg x = (a * x + c)
        a = 1664525
        c = 1013904223

它以0.2秒的速度运行,因此速度要快20倍。

正如@WillemVanOnsem所说,用GHC术语来说,35kB的内存是微不足道的。无论您有什么性能问题,它都与这一小块固定内存无关。起初,我说这可能是向量,但那是错误的。文本使用固定内存,但Data.Vector不使用。这段固定内存看起来像是来自运行时系统本身,所以您可以忽略它,如下所示

在GHC代码中,总分配是处理的度量。GHC程序是一个分配引擎。如果它没有分配,它可能没有做任何事情,很少有例外。所以,如果您希望您的算法能够按时运行,那么它也将在总分配中运行,通常相当于千兆字节

对于极少数例外情况,如果积极优化允许使用完全非固定值进行计算,GHC程序可以在恒定总分配但非恒定时间下运行。例如:

main = print (sum [1..10000000] :: Int)
以恒定的总分配(例如,堆上分配的50kB)运行,因为整数可以取消绑定。作为比较

main = print (sum [1..10000000] :: Integer)
在总分配上运行,例如在堆上分配320MB。顺便说一下,试着分析最后一个程序,并增加计数,直到它运行足够长的时间来生成几秒钟的分析数据。您将看到,它使用的固定内存量与您的程序相同,并且该内存量实际上不会随着上限而改变。所以,这只是运行时系统开销

回到你的例子。。。如果您担心性能,罪魁祸首可能是System.Random。这是一个非常慢的随机数生成器。如果我用n=10000000运行你的程序,需要4秒。如果我用简单的LCG替换随机数生成器:

randoms :: Word32 -> [Word32]
randoms seed = tail $ iterate lcg seed
  where lcg x = (a * x + c)
        a = 1664525
        c = 1013904223

它以0.2秒的速度运行,因此速度提高了20倍。

这是35kB,看起来不太像。也许你应该试着用更大的向量和数组来运行程序,看看内存配置文件是如何查找的。@WillemVanOnsem驻留增长非常缓慢:例如,如果我将10^6改为10^7,它将从44 Kb变为46 Kb,但我已经不得不等待12秒,这是不可忽略的。我可能应该提到我的另一个担忧:总分配随着n线性增长:-但我认为这可能与固定的内容有关。这是35kB,看起来不太像。也许你应该试着用更大的向量和数组来运行程序,看看内存配置文件是如何查找的。@WillemVanOnsem驻留增长非常缓慢:例如,如果我将10^6改为10^7,它将从44 Kb变为46 Kb,但我已经不得不等待12秒,这是不可忽略的。我可能应该提到我的另一个担忧:总分配与n成线性增长
:-但我想这可能与钉住的东西有关。非常感谢,这很有帮助。如果可能的话,但不一定是Haskell/GHC中的O1分配,您是否仍能想到这样一个场景,即按照示例的思路使用On算法?那会使你的回答对我更有洞察力。我想我还是会接受的。谢谢!实际上,我希望你在我的程序中所展示的那种优化。我认为随机数也应该适合拆箱…非常感谢,这是非常有用的。如果可能的话,但不一定是Haskell/GHC中的O1分配,您是否仍能想到这样一个场景,即按照示例的思路使用On算法?那会使你的回答对我更有洞察力。我想我还是会接受的。谢谢!实际上,我希望你在我的程序中所展示的那种优化。我认为随机数也应该适合拆箱…