[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