Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

With Nix, how can I specify Haskell dependencies with profiling enabled?

Tags:

haskell

nix

I was trying like this initially:

nix-shell -p "haskell.packages.ghc821.ghcWithPackages (p: with p; [text hspec lens])" -j4 --run 'ghc Main.hs -prof

Then GHC told me

Main.hs:4:1: error:
    Could not find module ‘Control.Lens’
    Perhaps you haven't installed the profiling libraries for package ‘lens-4.15.4’?
    Use -v to see a list of the files searched for.

Searching around the web I found this: https://github.com/NixOS/nixpkgs/issues/22340

So it seems I won't be able to download from the cache. But that's okay, if at least I can build the profiled variants locally.

Can I do that somehow simply by modifying the nix expression given to -p slightly?

Then at this point in writing this question, I remembered this resource: https://github.com/NixOS/nixpkgs/blob/bd6ba7/pkgs/development/haskell-modules/lib.nix

Where I found enableLibraryProfiling. So I tried:

nix-shell -p "haskell.packages.ghc821.ghcWithPackages (p: with p; [text hspec (haskell.lib.enableLibraryProfiling lens)])" -j4 --run 'ghc Main.hs -prof'

Which got me to a new error:

src/Control/Lens/Internal/Getter.hs:26:1: error:
    Could not find module ‘Data.Functor.Contravariant’
    Perhaps you haven't installed the profiling libraries for package ‘contravariant-1.4’?
    Use -v to see a list of the files searched for.
   |
26 | import Data.Functor.Contravariant
   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

So if I could map over all packages to enableLibraryProfiling on them, then I guess this could work. But my nix knowledge doesn't quite extend that far at the moment. How could I do that? And is this even the correct path to pursue?

like image 751
Wizek Avatar asked Dec 04 '17 15:12

Wizek


People also ask

Is nix written in Haskell?

nix developed by IOHK to build Haskell programs for Nix.

What is Haskell nix?

nix is infrastructure for building Haskell packages with Nix. haskell. nix can automatically translate your Cabal or Stack project and its dependencies into Nix code.

How do you use nix flakes?

Basic project usageIn your repo, run nix flake init to generate the flake. nix file. Then run git add flake. nix to add it to the git staging area, otherwise nix will not recognize that the file exists.

What is nix language?

The Nix Expression Language is a dynamic, functional and lazy domain-specific language used to instruct Nix how to build packages. Benefits of Nix programming language are reproducibility, binary caching, multiple versions, distributed and non-privileged builds.


2 Answers

With further snooping around in nix-repl and some helpful pointers from ElvishJerricco on #reflex-frp at FreeNode, I was able to construct this, which seems to work:

$ nix-shell -p "(haskell.packages.ghc821.extend (self: super: {mkDerivation = expr: super.mkDerivation (expr // { enableLibraryProfiling = true; });})).ghcWithPackages (p: with p; [text hspec lens])" -j4 --run 'ghc Main.hs -prof'

like image 176
Wizek Avatar answered Oct 19 '22 02:10

Wizek


I figured out a simple approach that I'm working into a largest blog post on Haskell development using Nix. For now, here's the text of just the profiling section:

Nix makes this fairly easy. First, we add the following to a ~/.config/nixpkgs/config.nix:

{
  packageOverrides = super: let self = super.pkgs; in
  {
    profiledHaskellPackages = self.haskellPackages.override {
      overrides = self: super: {
        mkDerivation = args: super.mkDerivation (args // {
          enableLibraryProfiling = true;
        });
      };
    };
  };
}

Now in the project we want to profile, we create a new profiling-shell.nix:

let nixpkgs = import <nixpkgs> {};
    orig = nixpkgs.pkgs.profiledHaskellPackages.callPackage ./default.nix {};
in (nixpkgs.pkgs.haskell.lib.doBenchmark orig).env

Almost identical to our normal shell.nix, except for the usage of profiledHaskellPackages, which we just defined globally. Now, an invocation of nix-shell profiling-shell.nix will rebuild every dependency in our project with profiling enabled. The first time this is done it will take quite a long time. Luckily this doesn't corrupt our Nix store - a vanilla nix-shell does seem to present us with our regular dependencies again, without redownloading or rebuilding.

WARNING: A nix-collect-garbage -d will wipe away all the custom-built libs from our Nix Store, and we'd have to build them again if they're needed.

If we're writing a library, the closest executable on hand that we could profile would be our benchmark suite. To do that:

  • Add -prof and -fprof-auto to our benchmark's GHC options
  • Regenerate default.nix
  • Enter our profiling shell: nix-shell profiling-shell.nix
  • cabal configure --enable-library-profiling --enable-benchmarks
  • cabal build
  • dist/build/projname/projname-bench +RTS -p
  • Look at the produced projname-bench.prof file

Based on the results, we can make code changes, remove the profiling options, regenerate default.nix, and benchmark as usual in our normal Nix Shell.

like image 31
Colin Woodbury Avatar answered Oct 19 '22 02:10

Colin Woodbury