
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_e... 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