使用 Stack 分析构建

Profiling builds with Stack

如何告诉 stack 使用 -prof 构建我的可执行文件及其所有依赖项?

仅仅将它添加到 .cabal 文件中的 ghc-options 是不够的,因为它只尝试构建启用分析的可执行文件,但失败了。

使用 Stack 1.0.0 和更新版本分析构建

要在启用分析的情况下构建:

stack build --profile

您可能需要先 运行 stack clean,但是 this should be fixed in Stack 1.5.0.

个人资料:

stack exec --profile -- <your program> +RTS <profiling options>

对于 <profiling options>,您可能需要 -p 进行时间分析或 -h 进行内存分析。对于时间分析,分析显示在 ./<your program>.prof 中,对于内存分析,分析显示在 ./<your program>.hp.

有关更多分析选项,请参阅 GHC profiling documentation

避免不必要的本地包重建(已在 Stack 2.X 中修复?)

由于 长期存在的堆栈 问题, 在分析和非分析构建之间切换可能会导致很多 不必要的本地包重建和 extra-deps。上班 围绕这一点,您可以使用单独的构建缓存进行分析和 非分析构建。例如,您将 stack <cmd> 用于 您可以使用非分析

stack --work-dir .stack-work-profile --profile <cmd>

用于 <cmd> 的分析版本。这使用一个单独的 在 .stack-work-profile 中缓存以分析工件, 而非分析工件将保留在默认情况下 .stack-work缓存。

使用 1.0.0 之前的 Stack 版本分析构建(即从 2015 年开始)

要在启用分析的情况下构建:

stack build --executable-profiling --library-profiling --ghc-options="-fprof-auto -rtsopts"

个人资料:

stack exec -- <your program> +RTS <profiling options>

Stack 1.0.0 及更新版本的示例

假设您有一个名为 test 的包,其中包含一个由 main 定义的可执行文件 test

module Main where

main :: IO ()
main = do
  print $ foo 0

foo :: Int -> Int
foo x = fooSub (x+1)
  where
    fooSub x = bar (x+1)

bar :: Int -> Int
bar x = barSub (x+1)
  where
    barSub x = barSubSub (x+1)
      where
        barSubSub x = x+1

然后执行 stack build --profile && stack exec -- test +RTS -p 将生成一个文件 ./test.prof 其中包括

                                                                                                individual      inherited
COST CENTRE                 MODULE                SRC                        no.     entries  %time %alloc   %time %alloc

  [... many lines omitted ...]
  main                      Main                  src/Main.hs:(4,1)-(5,15)    97          0    0.0    0.0     0.0    0.0
   foo                      Main                  src/Main.hs:(8,1)-(10,24)   98          1    0.0    0.0     0.0    0.0
    foo.fooSub              Main                  src/Main.hs:10:5-24         99          1    0.0    0.0     0.0    0.0
     bar                    Main                  src/Main.hs:(13,1)-(17,46) 100          1    0.0    0.0     0.0    0.0
      bar.barSub            Main                  src/Main.hs:(15,5)-(17,46) 101          1    0.0    0.0     0.0    0.0
       bar.barSub.barSubSub Main                  src/Main.hs:17:9-46        102          1    0.0    0.0     0.0    0.0
 main                       Main                  src/Main.hs:(4,1)-(5,15)    95          0    0.0   20.5     0.0   20.5

即,所有定义都有分析信息,包括 where 子句中的局部定义。

如果您只想分析顶级定义,您可以使用 GHC 选项 -fprof-auto-top 相反:做 stack build --profile --ghc-options=-fprof-auto-top && stack exec -- test +RTS -p 会产生一个 ./test.prof 其中包括

                                                                                individual      inherited
COST CENTRE MODULE                SRC                        no.     entries  %time %alloc   %time %alloc

 [... many lines omitted ...]
  main      Main                  src/Main.hs:(4,1)-(5,15)    97          0    0.0    0.0     0.0    0.0
   foo      Main                  src/Main.hs:(8,1)-(10,24)   98          1    0.0    0.0     0.0    0.0
    bar     Main                  src/Main.hs:(13,1)-(17,46)  99          1    0.0    0.0     0.0    0.0
 main       Main                  src/Main.hs:(4,1)-(5,15)    95          0    0.0   20.5     0.0   20.5

相反。

最后,注意 stack build --profile 也打开堆栈 痕迹。如果你改变程序使得barSubSub x = error $ show x,然后运行宁stack build --profile && stack exec test产生

test: 4
CallStack (from HasCallStack):
  error, called at src/Main.hs:17:23 in main:Main
CallStack (from -prof):
  Main.bar.barSub.barSubSub (src/Main.hs:17:9-36)
  Main.bar.barSub (src/Main.hs:(15,5)-(17,36))
  Main.bar (src/Main.hs:(13,1)-(17,36))
  Main.foo.fooSub (src/Main.hs:10:5-24)
  Main.foo (src/Main.hs:(8,1)-(10,24))
  Main.main (src/Main.hs:(4,1)-(5,15))
  Main.CAF:lvl8_r4Fc (<no location info>)

很酷!

我也遇到了这个问题,发现问题出在调用上:

stack exec my-exe +RTS -p-p 传递给堆栈而不是 my-exe。这有效:

stack exec -- my-exe +RTS -p

对于 stack buildstack benchstack test,您可以只使用 stack build/bench/test --profile。您可能必须先 stack clean 才能使用分析重新编译它。

对于 stack build,您仍然需要通过 +RTS -p 或您需要的任何选项(参见 GHC User Guide) when running the executable as in

您还可以在 debugging section of the stack user guide 中找到更多信息。

假设一个名为 project-name 的项目,这就是我获取时间和堆配置文件(带有颜色)的方式:

  1. 将依赖项添加到 project-name.cabal
  2. build-depends 部分
  3. 获取依赖包:stack build
  4. 从内部project-name/appprofiling enabled编译程序:stack ghc -- -prof -fprof-auto -rtsopts -O2 Main.hs
  5. 然后创建 heap and time 配置文件 ./Main +RTS -hc -p。这将产生 Main.hpMain.prof
  6. 将堆配置文件转换为 PostScript file,然后转换为具有以下颜色的 PDF 图表:stack exec -- hp2ps -c Main.hp && ps2pdf Main.ps

这是 PDF 中的堆配置文件: