
On Fri, Mar 4, 2016 at 8:30 AM, Eric Seidel
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.
Hmm, is it no longer an implicit parameter? I thought the new HasCallStack stuff was just a constraint synonym like type HasCallStack = (?callStack :: CallStack) For the most part the distinction doesn't matter, but it does when it comes to constraints in instance heads. The issue is that constraints are not solved at their callsites. Do we want `HasCallStack` to be able to be present in an instance head, and potentially get a location quite different from the actual use site? I would find this confusing, though sometimes helpful. Implicit parameters are a good match for this callstack stuff, because they have the restriction that they cannot be used as supeclass constraints. Does your work on the callstack stuff (thanks!) introduce backwards compatibility issues? I'm working on a library that will make extensive use of 7.10's implementation of callstacks.
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 _______________________________________________ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe