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

Reply via email to