[Haskell-cafe] HasCallStack - runtime costs?

Eric Seidel eric at seidel.io
Fri Mar 4 16:30:55 UTC 2016


On Fri, Mar 4, 2016, at 06:53, Johannes Waldmann wrote:
> Dear Cafe,
> 
> the new (8.*) call stack feature
> https://downloads.haskell.org/~ghc/8.0.1-rc2/docs/html/users_guide/glasgow_exts.html#hascallstack
> is certainly nice for debugging during development.
> 
> But how costly is it at runtime? I notice a 5 percent slowdown.

HasCallStack is really just a type class with a couple special rules for
building dictionaries in GHC's constraint solver. So at runtime each
function with a HasCallStack constraint takes an extra CallStack
argument. I don't know how to quantify the performance implications
beyond that, but you're right that HasCallStack is not free.

> That's not a problem if there's an easy way to switch
> this off for production (without changing the code).

Since HasCallStack is not a feature of the RTS, but actually part of the
generated code, there's not really an easy way to disable it without
changing the code. As much as I dislike CPP, I think it's the best
solution for a toggleable HasCallStack, something like

#if DEBUG
#define HASCALLSTACK (HasCallStack)
#else
#define HASCALLSTACK ()
#endif

foo :: HASCALLSTACK => a -> b

ought to work.
 
> Related: how to make code that uses it,
> compile with older ghcs that don't have it.
> 
> I made this hack: do not import GHC.Stack.Types, but instead
> 
> {-# language CPP, MultiParamTypeClasses #-}
> 
> #if (__GLASGOW_HASKELL__ < 710)
> {-# language NullaryTypeClasses #-}
> #endif
> 
> module Stack
> ( HasCallStack )
> where
> 
> #if (__GLASGOW_HASKELL__ >= 800)
> import GHC.Stack.Types
> #else
> class HasCallStack
> instance HasCallStack
> #endif

This might be a nice addition to the base-compat package.

> When I compile with 8.rc2, and change ">= 800" to ">= 900",
> I am getting the 5 percent speedup mentioned above.
> 
> But does it really do what I hope it does
> (remove all runtime overhead that call stacks may have)?

It should remove all the overhead of call stacks for calling functions
you wrote. If you import a function with a HasCallStack constraint
there's no way to disable the overhead for that function (for good
reason, it might use the CallStack!).

> When I compile with 7.10.3, I am getting 5 .. 10 percent faster again.
> 
> My code does nothing fancy (w.r.t. types and libraries),
> it just uses Data.IntMap heavily. And it has some
> 
> class Semiring s where
>   zero :: s
>   one  :: s
>   plus :: HasCallStack => s -> s -> s
>   times :: HasCallStack => s -> s -> s

I'm curious, why do plus and times take a CallStack? I wouldn't expect
them to be partial, so it seems like unnecessary overhead.

Eric


More information about the Haskell-Cafe mailing list