Profiling builds with Stack
Asked Answered
N

5

72

How do I tell stack to build my executable and all its dependencies with -prof?

Simply adding it to ghc-options in the .cabal file is not enough, because it only tries to build the executable with profiling enabled, which fails.

Nehemiah answered 20/8, 2015 at 16:24 Comment(0)
P
118

Profiling builds with Stack 1.0.0 and newer

To build with profiling enabled:

stack build --profile

You may need to run stack clean first, but this should be fixed in Stack 1.5.0.

To profile:

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

where for <profiling options> you might want -p for time profiling or -h for memory profiling. For time profiling, the profile appears in ./<your program>.prof, and for memory profiling, the profile appears in ./<your program>.hp.

See GHC profiling documentation for more profiling options.

Avoiding unnecessary rebuilding of local packages (fixed in Stack 2.X?)

Due to a long standing Stack issue, switching between profiling and non-profiling builds can cause a lot of unnecessary rebuilding of local packages and extra-deps. To work around this, you can use separate build caches for your profiling and non-profiling builds. For example, where you use stack <cmd> for non profiling you can use

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

for a profiling version of <cmd>. This uses a separate cache in .stack-work-profile for profiling artifacts, whereas non profiling artifacts will be preserved in the default .stack-work cache.

Profiling builds with Stack versions before 1.0.0 (i.e. from 2015)

To build with profiling enabled:

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

To profile:

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

Example for Stack 1.0.0 and newer

Suppose you have a package called test with a single executable test defined by main here:

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

then doing stack build --profile && stack exec -- test +RTS -p will produce a file ./test.prof which includes

                                                                                                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

I.e., there is profiling information for all definitions, including local definitions in where clauses.

If you only want to profile top-level definitions, you can build with the GHC option -fprof-auto-top instead: doing stack build --profile --ghc-options=-fprof-auto-top && stack exec -- test +RTS -p produces a ./test.prof which includes

                                                                                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

instead.

Finally, note that stack build --profile also turns on stack traces. If you change the program so that barSubSub x = error $ show x, then running stack build --profile && stack exec test produces

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>)

Pretty cool!

Predator answered 20/8, 2015 at 23:19 Comment(9)
This doesn't work. Using stack exec my-exe +RTS -p shows executables haven't been compiled with -prof, and trying to do so shows that ld cannot find the profiled versions of the libraries.Globose
This worked for me: stack install --enable-executable-profiling --enable-library-profiling --ghc-options="-fprof-auto -rtsopts"Engraving
How do you execute it / profile it after the build has finished?Demagogic
@ChrisStryczynski: I just edited my answer. Hope that helps.Predator
Could you put the part about .stack-work-profile first? The first time I read this answer, I saw the first line and did it, and regretted it once I read on.Kirchhoff
Hi from 2019! Today with Stack 2.1.3, these instructions did not work for me. I needed to add --profile to the stack exec call as well. This worked: stack build --profile && stack exec --profile -- my-exe +RTS ...Juristic
@Calvin: thanks for the heads up! Ijust updated my answer to include --profile in the stack exec command. My brief testing indicates that Stack 2.x versions are smarter than before about profiling, automatically keeping the regular and --profile versions separate, but I can't find the documentation for this. I believe this is the relevant Stack issue, but it's still open: github.com/commercialhaskell/stack/issues/4032Predator
What do the numbers mean in src/Main.hs:(4,1)-(5,15)? I initially thought they're range of line numbers but they dont make sense; also they dont seem to match in my case.. The first of each pair may define a range of lines?Caressive
Ok. It seems they indicate the pair of the cursor locations.. as in the code-snippet from and to ..Caressive
H
14

I had this problem as well and found that the problem was in the invocation:

stack exec my-exe +RTS -p passes -p to stack instead of my-exe. This works:

stack exec -- my-exe +RTS -p
Hepza answered 8/1, 2016 at 14:3 Comment(0)
V
6

For stack build, stack bench and stack test you can just use stack build/bench/test --profile. You may have to stack clean first to get it to recompile with profiling.

For stack build you will still have to pass +RTS -p or whatever option you need (see GHC User Guide) when running the executable as in @Tomáš Janoušek answer.

You can also find more information in the debugging section of the stack user guide.

Vie answered 1/12, 2016 at 23:20 Comment(2)
You can also add "--force-dirty" to the end of your stack build command to force it to recompile.Celisse
did you mean to say stack exec?Overspend
U
3

Assuming a project called project-name, this is how I get a time and heap profile (with colors):

  1. Add dependencies to the build-depends section of project-name.cabal
  2. Get dependent packages: stack build
  3. From inside project-name/app compile the program with profiling enabled: stack ghc -- -prof -fprof-auto -rtsopts -O2 Main.hs
  4. Then create the heap and time profile ./Main +RTS -hc -p. This will produce Main.hp and Main.prof
  5. Turn the heap profile into a PostScript file and then into a PDF chart with colors: stack exec -- hp2ps -c Main.hp && ps2pdf Main.ps

That's the heap profile from the PDF:

heap_profile_with_colors

Unfasten answered 11/4, 2019 at 11:16 Comment(1)
@LupusOssorum: I don't know if there's a way to turn a .prof file into PDF. But there are visualization tools for .prof files such as profiteur, professor, and viewprof.Unfasten
L
2

The concise way to run Stack project with profiling is to use stack run command, e.g.:

stack run --profile -- +RTS -p

This command will first build your project with profiling enabled and then execute it with +RTS -p flag.

Note that if you use stack exec command instead you need to first invoke stack build and re-invoke it every time your source changes since stack exec does not rebuild project even if it is stale.
On the other hand stack run will automatically rebuild your project before executing it if there is a pending change.

Libertinism answered 29/3 at 2:9 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.