Thank you for the detailed explanation! Would it make sense to run the catch blocks/finalizers before unwinding the stack? I.e. each `try` pushes its catch/finally block as a "closure" onto a separate stack, to be run when an exception occurs, then the stack is only unwound once regular control flow is resumed, or collected if no handler was found. I was wondering how Common Lisp was able to provide restarts, this seems like a reasonable implementation strategy.
On Saturday, May 7, 2016 at 10:28:18 AM UTC-4, Yichao Yu wrote: > > On Sat, May 7, 2016 at 8:46 AM, Cedric St-Jean <cedric...@gmail.com > <javascript:>> wrote: > >> mostly due to various technical reason > > In arbitrary order, the ones I can think of now. > > 1. We collect backtrace > > There are many cases where we don't actually need this info, but > we need better code analysis to be able to figure that out. > > 2. We don't have zero cost exception frame so we need to spill many > registers. > > It's hard for us to have zero cost exception frame unless all the > C libraries are compiled with unwind info. Also note that zero cost > exception frame means even more expensive exception throwing since the > registers are not explicitly saved and we need to unwind the call > stack to restore them. This is a trade off after all. > > 3. Preserving local variables across an exception requires spilling it > to stack and this adds some hidden cost. > > It's a little hard to say how much cost this have, probably not > THAT much unless you have a lot of variables that is modified in try > and used after the exception is caught. Very likely strongly depending > on the code pattern. > > 4. The GC frame allocation is global (well, per-function) instead of > control flow local. > > This cause allocation in not-taken branch to affect the > performance on the taken branch. This is not specific to exceptions > but is particularly bad for exceptions since throwing one sometimes > requires allocation that otherwise doesn't exist in the function (e.g. > `ArgumentError("My error message with $variable spliced in")`) and > these branches are meant to be not taken most of the time (if the > error can happen most of the time, you'd be better off with a error > code return, since exceptions are not designed for that). > > > > > Could you please go into those? I'd like to understand why unwinding the > > stack is costlier than returning from a function. > > > > On Saturday, May 7, 2016 at 8:38:03 AM UTC-4, Yichao Yu wrote: > >> > >> On Sat, May 7, 2016 at 6:58 AM, Ben Ward <axolotl...@gmail.com> wrote: > >> > Hi, > >> > > >> > I have a question which may be laughable to CS people (be gentle, I'm > a > >> > Biologist), but we know from guidelines that Julia performs best, > when a > >> > method always returns the same type of value. > >> > So the Julia type inference knows - ok, use this method with an Int, > it > >> > will > >> > always return a bool say. How does throws fit into this? To my mind > this > >> > means the method used with an Int may return a bool, but it also may > >> > result > >> > in a type of (e.g.) ArgumentError getting thrown. So how do throws in > >> > functions affect performance and julian's ability to infer types and > >> > optimise code (if at all?)? > >> > >> While throwing or catching an error itself is slow (mostly due to > >> various technical reason) they shouldn't affect type stability. > >> > >> The type inference currently do not reason about what type of > >> exception can be thrown and even if it does, this won't affect what we > >> usually call the type stability of a function since it is only about > >> the **return type** of a function and throwing an error is not return > >> (in the sense that `a = cond ? error() : 1`, if the next statement is > >> executed, a is always a `Int`). > >> > >> > > >> > Thanks, > >> > Ben W. >