On Nov 25, 2007, at 11:49, Jon Harrop wrote:> On Sunday 25 November 2007 12:23, Gordon Henriksen wrote: > >> On 2007-11-24, at 21:58, Jon Harrop wrote: >> >>> - Exceptions >> >> http://llvm.org/docs/ExceptionHandling.html >> >> LLVM's exception support is tuned toward DWARF "zero-cost >> exceptions," i.e. C++ exception handling. Anton Korobeynikov and >> Duncan Sands (who is working on Ada) are probably the experts in >> this area. > > Excellent. > > So does zero-cost exception handling in C++ refer to a special case > where you can statically prove that there are no destructors to > call, or something?"Zero cost" refers to the overhead introduced in the case when an exception is not thrown at all. C++ has a guiding principal that "you don't pay for it if you don't use it." In the case of exceptions, this had pretty far-reaching implications, since each stack-based object calls for a destructor cleanup block. The resolution was for implementations to introduce no runtime overhead whatsoever for a try/catch block if no exception is raised, thus "zero cost". Throw actually became more expensive as a result of these implementations.> There is one thing that confuses me about this though. I benchmarked > exception handling in OCaml and C++ a while ago and found OCaml to > be ~6x faster and the best explanation I got was that C++ does not > have zero-cost exceptionsMicrobenchmarking raise vs. throw is not very realistic. ZCEH makes a complicated performance tradeoff wherein the exceptional case is penalized in order to optimize the common case. Since C++ code has many implicit try/finally blocks (each stack-based object with a destructor), this is a clear win. Ocaml code is quite different.> because it requires destructors to be called as the stack is > unwound, whereas OCaml can just jump back and leave collection to > the GC.GC in general will reduce the amount of cleanup code, which should reduce DWARF EH's actual overhead (fewer cleanup blocks means smaller tables and accordingly cheaper unwinds), especially when unwinding deep stacks. Objective-C 2 on Leopard may be one of the few systems which has this combination of properties. (But exceptions in Cocoa code are discouraged even more strongly than in C++.) Note that there is nothing in LLVM that prevents alternative exception handling regimes, they just won't benefit from the existing infrastructure, and will not interoperate with the DWARF runtime. — Gordon
On Nov 25, 2007, at 2:00 PM, Jon Harrop wrote:> Ok. Might as well start by reusing as much as possible. Exceptions > are very > common in OCaml though. Is my 6x result a fair quantitative estimate > of how > much faster exceptions could be make in this system for my compiler > if it > were customized?The idea behind the implementation of C++ exceptions is that C++ exceptions handle "exceptional" conditions, not normal code path. I.e. If you're throwing an exception in the course of normal execution, there's something wrong with your code. This means that C++ exceptions handle the normal case very well (no overhead), but that the exceptional condition is OK with being slower as a tradeoff. That said, I don't know much about OCaml and so don't know if this philosophy fits the standard OCaml programming style. If throw/catch/etc are meant to be normal path of execution then a small cost up front in, for example, try blocks could easily speed up execution later when you hit the throw. -eric
> I'm not quite sure I understand this (I don't even know what DWARF EH is!) > but > are "cleanup blocks" the construct that I would be using to build > exception > handlers?DWARF 2 is is the debug information format that goes with ELF, part of TIS (the Tool Interface Standard) :- http://www.x86.org/ftp/manuals/tools/dwarf.pdf http://www.x86.org/ftp/manuals/tools/elf.pdf EH is Exception Handling :) Aaron
On Nov 25, 2007, at 17:00, Jon Harrop wrote:> On Sunday 25 November 2007 21:40, Gordon Henriksen wrote: > >> Microbenchmarking raise vs. throw is not very realistic... > > Just to clarify: the benchmark compared the performance of RB tree > implementations using exceptions to terminate early when an already- > present element was inserted. I found that it is more efficient in C+ > + (using g++) to mimic the exception using nested "if"s rather than > throw and catch. Although simple, this benchmark was of practical > importance to me at the time.Yes. Not surprising.>> GC in general will reduce the amount of cleanup code, which should >> reduce DWARF EH's actual overhead (fewer cleanup blocks means >> smaller tables and accordingly cheaper unwinds), especially when >> unwinding deep stacks. > > I'm not quite sure I understand this (I don't even know what DWARF > EH is!) but are "cleanup blocks" the construct that I would be using > to build exception handlers?Yes. I should've used the term landing pads (http://llvm.org/docs/ExceptionHandling.html#try_catch ).>> Note that there is nothing in LLVM that prevents alternative >> exception handling regimes, they just won't benefit from the >> existing infrastructure, and will not interoperate with the DWARF >> runtime. > > Ok. Might as well start by reusing as much as possible. Exceptions > are very common in OCaml though. Is my 6x result a fair quantitative > estimate of how much faster exceptions could be make in this system > for my compiler if it were customized?Likely so. To a reasonable approximation, LLVM EH is C++ EH.> If so, I don't think that is a problem: exceptions are 600x slower > in F# than in OCaml!:) — Gordon
>> Note that there is nothing in LLVM that prevents alternative >> exception >> handling regimes, they just won't benefit from the existing >> infrastructure, and will not interoperate with the DWARF runtime. > > Ok. Might as well start by reusing as much as possible. Exceptions > are very > common in OCaml though. Is my 6x result a fair quantitative estimate > of how > much faster exceptions could be make in this system for my compiler > if it > were customized?If Ocaml has no landing pads, and if "catch" blocks are infrequent, it would be very reasonable to implement them in terms of setjmp and longjmp (as Gordon is alluding to). This is pretty straight-forward to do, and lets you evaluate the cost tradeoff. The nice thing about LLVM is that it doesn't force you to pick one specific implementation strategy, you can use the one that makes the most sense for your language. -Chris