1 How to analyze Haskell performance
2 ==================================
4 When a Haskell application is slow or uses too much memory,
5 Cabal and `GHC <https://downloads.haskell.org/ghc/latest/docs/users_guide/profiling.html>`__
6 can help you understand why. The main steps are:
8 1. Configure the project in a way that makes GHC insert performance-measuring code into your application.
9 2. Run the application with the right
10 `runtime system (RTS) flags <https://downloads.haskell.org/ghc/latest/docs/users_guide/runtime_control.html>`__
11 to produce a performance report.
12 3. Visualize and analyze that report.
14 The process of inserting performance measuring code and collecting performance information
15 is called "profiling".
16 This guide describes how to instruct Cabal to pass desired profiling flags to the GHC compiler;
17 Cabal acts as a convenient build configuration interface while the work is done by GHC.
18 To get a deeper understanding of the overall profiling process itself in GHC,
19 it is highly recommended to read in depth the
20 `Profiling section in GHC's User Guide <https://downloads.haskell.org/ghc/latest/docs/users_guide/profiling.html>`__.
22 Profiling CPU performance
23 -------------------------
25 First, configure Cabal to build your application, e.g. ``my-app``, with profiling enabled,
26 with the following command:
28 .. code-block:: console
30 $ cabal configure --enable-profiling
32 This command creates a ``cabal.project.local`` file with the following content:
38 This file stores temporary configuration settings that are passed implicitly to further Cabal commands
39 like ``cabal build`` and ``cabal run``.
40 The setting ``profiling: True`` tells GHC to build your application (and its dependencies) with profiling enabled,
41 and to insert performance measuring code into your application.
42 Where exactly such code is inserted can be controlled with settings like ``profiling-detail``
43 that are presented later.
44 Further in-depth information on profiling with GHC and its compiler options can be found in the
45 `GHC profiling guide <https://downloads.haskell.org/ghc/latest/docs/users_guide/profiling.html>`__
49 While a :ref:`cabal.project <cabal-project-file>` file is intended for long-time settings
50 that are useful to store in Git, ``cabal.project.local`` is for short-lived, local experiments
51 (like profiling) that, in general, shouldn't be committed to Git.
53 Second, run your application with the right runtime system flags and let it create a profiling report:
55 .. code-block:: console
57 $ cabal run my-app +RTS -pj -RTS
58 <app builds, runs and finishes>
60 When the application finishes, a profiling JSON report (due to option ``-pj``)
61 is written to a ``<app-name>.prof`` file, i.e. ``my-app.prof``, in the current directory.
65 Different report formats can be generated by using different RTS flags. Some useful ones are:
67 - ``-p`` for a GHC's own
68 `standard report <https://downloads.haskell.org/ghc/latest/docs/users_guide/profiling.html#cost-centres-and-cost-centre-stacks>`__
69 ``<app-name>.prof``, which can be visualized with `profiteur <https://github.com/jaspervdj/profiteur>`__
70 or `ghcprofview <https://github.com/portnov/ghcprofview-hs>`__.
72 `JSON report <https://downloads.haskell.org/ghc/latest/docs/users_guide/profiling.html#json-profile-format>`__
73 ``<app-name>.prof``, which can be visualized with `Speedscope <https://speedscope.app>`__.
74 - ``-l -p`` for a binary
75 `"eventlog" report <https://downloads.haskell.org/ghc/latest/docs/users_guide/runtime_control.html#rts-eventlog>`__
76 ``<app-name>.eventlog``, which contains a lot more details and can show you resource usage over time, and can
77 be converted to JSON with `hs-speedscope <https://github.com/mpickering/hs-speedscope>`__
78 to be visualized with `Speedscope <https://speedscope.app>`__.
79 This will also generate a ``.prof`` file (due to ``-p``), which you can ignore.
80 We just need the ``-p`` flag for the ``.eventlog`` file to include profiling information.
82 Finally, visualize this JSON report ``my-app.prof`` and analyze it for performance bottlenecks.
83 One popular open-source
84 `flame graph <https://www.brendangregg.com/flamegraphs.html>`__
86 `Speedscope <https://speedscope.app>`__,
87 which runs in the browser and can open this JSON file directly.
89 `Haskell Optimization Handbook <https://haskell.foundation/hs-opt-handbook.github.io>`__
90 on how to optimize your code based on the profiling results afterwards.
92 So far, we’ve only used a single Cabal option to enable profiling in general for your application.
93 Where and when GHC should insert performance measuring code can be controlled with the ``profiling-detail`` setting
95 Leaving ``profiling-detail`` unspecified as before results in sensible defaults that differ between libraries and executable.
96 See the docs for :ref:`profiling-detail<profiling-detail>` to see which options are available.
97 You can provide ``profiling-detail`` settings and more compiler flags to GHC
98 (such as ``-fno-prof-count-entries``) via the ``cabal.project.local`` file:
100 .. code-block:: cabal
103 profiling-detail: late-toplevel
108 The setting ``profiling-detail: late-toplevel`` instructs GHC to use so-called
109 `late-cost-center profiling <https://downloads.haskell.org/ghc/latest/docs/users_guide/profiling.html#ghc-flag--fprof-late>`__
110 and insert measuring code only after important optimisations have been applied to your application code.
111 This reduces the performance slow-down of profiling itself and gives you more realistic measurements.
113 The ``program-options`` section allows you to add more settings like GHC options to the local
114 packages of your project (See :ref:`Program options<program_options>`).
115 The ``ghc-options`` setting allows you to further control which functions and other bindings
116 the GHC compiler should profile, as well as other aspects of profiling.
117 You can find more information and further options in the
118 `GHC "cost-center" guide <https://downloads.haskell.org/ghc/latest/docs/users_guide/profiling.html#automatically-placing-cost-centres>`__.
120 `GHC profiling compiler options <https://downloads.haskell.org/ghc/latest/docs/users_guide/profiling.html#compiler-options-for-profiling>`__
123 Profiling your dependencies too
124 -------------------------------
126 The profiling setup so far with the ``cabal.project.local`` file only applied to your local packages,
127 which is usually what you want.
128 However, bottlenecks may also exist in your dependencies, so you may want to profile those too.
130 First, to enable ``late``-cost-center profiling for all packages (including dependencies) concerning your project,
131 not just the local ones, add the following to your project’s ``cabal.project.local`` file:
133 .. code-block:: cabal
136 profiling-detail: late-toplevel
140 There are several keywords to specify to which parts of your project some settings should be applied:
142 - ``program-options`` to apply to :ref:`all local packages<program_options>`.
143 - ``package <package-name>`` to apply to a :ref:`single package<package-configuration-options>`, be it local or remote.
144 - ``package *`` to apply to :ref:`all local and remote packages (dependencies)<package-configuration-options>`.
146 Second, rerun your application with ``cabal run``, which also automatically rebuilds your application:
148 .. code-block:: console
150 $ cabal run my-app -- +RTS -pj -RTS
151 Resolving dependencies...
152 Build profile: -w ghc-9.10.1 -O1
153 In order, the following will be built (use -v for more details):
154 - base64-bytestring-1.2.1.0 (lib) --enable-profiling (requires build)
155 - cryptohash-sha256-0.11.102.1 (lib) --enable-profiling (requires build)
157 <app runs and finishes>
159 You can now find profiling data of dependencies in the report ``my-app.prof``
160 to analyze. More information on how to configure Cabal options can be found in the
161 :ref:`Cabal options sections <package-configuration-options>`.