New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Lifting allocation of constant exception constructors? #6203
Comments
Comment author: @garrigue Yes it would. By the way he is wrong about ocaml: in ocaml functors are applicative, |
Comment author: @gasche Pierre Chambart ongoing work on a big ball of inlining and value analysis (e.g. http://www.ocamlpro.com/blog/2013/07/11/inlining-progress-report.html ) has gotten some noticeable performance improvements from not allocating exceptions when possible (I hope that his changes are correct, but he may have some exception-specific transformations that make implicit semantic assumptions). Alain, if you are interested in this specific optimization, it may be interesting to contact Pierre and maybe start review some of his changes. |
Comment author: @lpw25 I'm a little confused here: I thought that creating a new exception constructor needed to be generative (as discussed in Bob Harper's blog), but that creating a particular reference to a constructor did not need to be generative. In other words, Not_found == Not_found could safely return true. This seems to be what Alain is talking about in this issue. I'm actually quite surprised that this is not already the case (it is true for my open types work). |
Comment author: @alainfrisch I can only concur with Leo's comment (and I was also surprised): while the exception declaration needs to be generative, I don't see why the application of a constant exception constructor should be. Jacques: how would you observe the difference between let not_found = Not_found let f x = .... raise not_found and let f x = .... raise Not_found without explicitly using (==) on exception values ? |
Comment author: @garrigue OK, I got confused with your naming it "constructor". Right, it seems that for constant exceptions the code allocates a 1-sized block containing this pointer. |
Comment author: @lpw25 In my open types work (where I am free to chose the representation) I represent constant constructors as just a pointer to the "constructor block", while other constructors are represented as blocks whose first field is a pointer to their "constructor block". This works because I ensure that the first field of all constructor blocks is 0, so it is safe to compare a constructor block with a normal constructor's block. I also tag the constructor blocks with the object tag and put a fresh id in their second field. This means that structural comparison works correctly on them (unlike exceptions: #4765). I think it would be a definite improvement to change the exception representation to this, although I don't know if it would break legacy C bindings. |
Comment author: @alainfrisch Physical identity of exception values is indeed used in the backtrace mechanism, see backtrace.c: void caml_stash_backtrace(value exn, code_t pc, value * sp) This is used to coalesce stack fragments upon re-raise of the caught exception. |
Comment author: @gasche Indeed, but I don't think sharing exception values would break any contract with users of the backtrace mechanism. My understanding of re-raising is that it's meant to improve readability (by trashing less backtraces), and that detecting more opportunity for re-raising would still be correct. I would consider it only a (tweakable) implementation detail that try e with Not_found as exn -> raise exn and try e with Not_found -> raise Not_found currently don't behave in the same way with respect to re-raising. |
Comment author: @alainfrisch I'm more concerned about situations where really unrelated exceptions (same generic constructor, raised from unrelated locations) are coalesced in a single meaningless trace. |
Comment author: @alainfrisch Here is an example:let x = Exit let f () = raise x let () =
|
Comment author: @lpw25 How about supporting re-raising of different exceptions with messages like: Raised "Foo" at file "exn.ml", line 3, characters 17-18 Then (in addition to providing an explicit re-raise function) we could simply treat all raises that are statically within a handler as re-raises. So: try is a re-raise, but: let f () = try isn't. This heuristic is very predictable for users, and the ability to track re-raises of different exceptions can be very useful for debugging some styles of code (e.g. parts of the compiler itself). |
Comment author: @alainfrisch
"reraise" is not exposed as a user-accessible function for now (although it is trivial to do so, in order to support abstracting the handler code). With this change, the "problematic" example above works fine (the two segments are not merged). One can of course build example where it breaks traces, but I don't expect this to be common, and on the other hand, it reduces the risk of getting bad traces and enable the optimization suggested in this ticket (no allocation on references to constant constructors). The branch also adds a "raise_notrace" function (see #5935). |
Comment author: @alainfrisch Commits 14235, 14236 on the "raise_variants" branch changes the representation of exceptions values built from constant exception constructors. The new representation is just the exception slot itself (i.e. the "string ref"). Here are the results for the following micro-benchmark (with ocamlopt, without -g, without OCAMLRUNPARAM=b=1): let () = trunk: 2.52s The difference cannot be observed with backtraces enabled, though, even if the version from the branch does not allocate at all (contrary to trunk). |
Comment author: @alainfrisch One point I did not consider is how the backtrace mechanism behaves in a multi-threaded application. I don't know what's the current behavior, but it is possible that the proposal (marking reraise statically to allow sharing constant exception constructor) does not play nice with threads. |
Comment author: @alainfrisch raise_variants branch has been merged into trunk (rev 14289). |
Original bug ID: 6203
Reporter: @alainfrisch
Assigned to: @alainfrisch
Status: closed (set by @xavierleroy on 2015-12-11T18:25:15Z)
Resolution: fixed
Priority: normal
Severity: minor
Fixed in version: 4.02.0+dev
Category: back end (clambda to assembly)
Related to: #5935
Monitored by: @gasche @diml @jmeber @yakobowski
Bug description
Constant exception constructors (Not_found, Exit) are quite common. It seems that any reference to such a constructor allocates a fresh exception value. Would it break something to pre-allocate such values (either by exporting them from the declaring module as an extra value, in addition to the exception slot itself; or by lifting their allocation at the top of the unit where they are used)? Or is the physical identity of exception values somehow used?
The text was updated successfully, but these errors were encountered: