Performance 在Haskell'中强制顺序处理;s Data.Binary.Get

Performance 在Haskell'中强制顺序处理;s Data.Binary.Get,performance,haskell,lazy-evaluation,Performance,Haskell,Lazy Evaluation,在尝试用Java类文件语言导入基本Java运行时库rt.jar之后,我发现它使用了大量内存 我已经将演示该问题的程序减少到100行,并将其上载到。如果不强制在第94行中计算流,我就没有机会运行它,因为它会耗尽我所有的内存。在将流传递给getClass之前强制执行stream完成,但仍然会占用大量内存: 34,302,587,664 bytes allocated in the heap 32,583,990,728 bytes copied during GC 139,810,

在尝试用Java类文件语言导入基本Java运行时库rt.jar之后,我发现它使用了大量内存

我已经将演示该问题的程序减少到100行,并将其上载到。如果不强制在第94行中计算
,我就没有机会运行它,因为它会耗尽我所有的内存。在将流传递给
getClass
之前强制执行
stream
完成,但仍然会占用大量内存:

  34,302,587,664 bytes allocated in the heap
  32,583,990,728 bytes copied during GC
     139,810,024 bytes maximum residency (398 sample(s))
      29,142,240 bytes maximum slop
             281 MB total memory in use (4 MB lost due to fragmentation)

  Generation 0: 64992 collections,     0 parallel, 38.07s, 37.94s elapsed
  Generation 1:   398 collections,     0 parallel, 25.87s, 27.78s elapsed

  INIT  time    0.01s  (  0.00s elapsed)
  MUT   time   37.22s  ( 36.85s elapsed)
  GC    time   63.94s  ( 65.72s elapsed)
  RP    time    0.00s  (  0.00s elapsed)
  PROF  time   13.00s  ( 13.18s elapsed)
  EXIT  time    0.00s  (  0.00s elapsed)
  Total time  114.17s  (115.76s elapsed)

  %GC time      56.0%  (56.8% elapsed)

  Alloc rate    921,369,531 bytes per MUT second

  Productivity  32.6% of total user, 32.2% of total elapsed
我认为问题在于
ConstTable
s停留在附近,所以我尝试强制
cls
在第94行。但这只会使内存消耗和运行时变得更糟:

  34,300,700,520 bytes allocated in the heap
  23,579,794,624 bytes copied during GC
     487,798,904 bytes maximum residency (423 sample(s))
      36,312,104 bytes maximum slop
             554 MB total memory in use (10 MB lost due to fragmentation)

  Generation 0: 64983 collections,     0 parallel, 71.19s, 71.48s elapsed
  Generation 1:   423 collections,     0 parallel, 344.74s, 353.01s elapsed

  INIT  time    0.01s  (  0.00s elapsed)
  MUT   time   40.60s  ( 42.38s elapsed)
  GC    time  415.93s  (424.49s elapsed)
  RP    time    0.00s  (  0.00s elapsed)
  PROF  time   56.53s  ( 57.71s elapsed)
  EXIT  time    0.00s  (  0.00s elapsed)
  Total time  513.07s  (524.58s elapsed)

  %GC time      81.1%  (80.9% elapsed)

  Alloc rate    844,636,801 bytes per MUT second

  Productivity   7.9% of total user, 7.7% of total elapsed

因此,我的问题基本上是,如何强制对涉及的文件进行顺序处理,以便在处理完每个文件后,只有字符串结果(
cls
)保留在内存中?

编辑2:我刚刚意识到您的代码会这样做:

stream <- BL.pack <$> fileContents [] classfile
可能有必要使用
deepseq
而不仅仅是
seq
,尽管我怀疑简单的
seq
在这里就足够了

然而,我认为有一个更好的解决方案,那就是使用
mapM\uu
而不是
mapM
。我认为创建一个函数来处理每个结果,而不是返回一个列表,通常是更好的风格(而且几乎总是更好的性能)。在此,您可以将主要功能更改为:

main = do 
  withArchive [CheckConsFlag] jarPath $ do
    classfiles <- filter isClassfile <$> fileNames []
    forM_ classfiles $ \classfile -> do 
      stream <- BL.pack <$> fileContents [] classfile
      let cls = runGet getClass stream
      lift $ print cls
main=do
withArchive[CheckConsFlag]jarPath$do
类文件做什么

谢谢你的建议

我认为对于我的具体问题,解决方案将是以小块方式处理.jar文件——幸运的是,内部类在.jar文件中总是与其外部类位于同一目录中,因此不需要在一次运行中处理所有50个meg


我唯一不能完全理解的是,是否可以通过枚举器使用libzip,或者是否需要一个新的libzip实现?

您在第94行强制计算cls的想法是正确的。但我猜你们的方法并没有成功。我的版本是40MB,而不是220MB

关键是强制还原到cls的正常形式,这是由rnf cls完成的。这必须在电话返回之前发生。因此:

rnf cls`seq`返回cls

或者,您可以使用Control.Exception.evaluate: 评估$rnf cls
return cls

您如何进行堆分析并在问题中发布该图。我已将这两个版本的堆分析输出添加到hpaste。您是否尝试过使用
grane
包?这只是一个想法,但是如果你知道文件足够小,严格的ByTestRing通常会有所帮助。我对
谷物
有两个问题:一个是它似乎不支持固定字节顺序的浮动,另一个是缺少MonadFix实例。但是在实际的程序中,我的结果是cls的列表。另外,第二个输出来自stream
seq
cls
seq
return cls(如果我不够清楚,很抱歉)@Cactus,很抱歉,我刚刚在我的答案顶部添加了一个注释,请检查。另外,我知道您的结果是cls的列表,但我认为最好是创建一个函数来处理每个cls,而不是创建一个cls列表。但我不认为这是最重要的问题。我使用的模块来自Binary、data-Binary-ieee754、bytestring、containers、missingh和libzip(希望我没有遗漏任何模块),我应该使用什么来代替BL.pack?使用BS.pack和BL.fromChunks会有帮助吗?
main = do 
  withArchive [CheckConsFlag] jarPath $ do
    classfiles <- filter isClassfile <$> fileNames []
    forM_ classfiles $ \classfile -> do 
      stream <- BL.pack <$> fileContents [] classfile
      let cls = runGet getClass stream
      lift $ print cls