www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Herb Sutter on zero cost exceptions for C++

reply shfit <shfit fake.de> writes:
Herb Sutter gave a pretty interesting talk on zero cost 
exceptions and RTTI.

https://m.youtube.com/watch?v=ARYP83yNAWk

Could this be an option for D to avoid  gc exceptions?
Sep 26
next sibling parent reply rikki cattermole <rikki cattermole.co.nz> writes:
On 27/09/2019 5:51 AM, shfit wrote:
 Herb Sutter gave a pretty interesting talk on zero cost exceptions and 
 RTTI.
 
 https://m.youtube.com/watch?v=ARYP83yNAWk
 
 Could this be an option for D to avoid  gc exceptions?
We have a solution. https://github.com/dlang/DIPs/blob/master/DIPs/other/DIP1008.md I've looked at the dmd and Phobos PR's, I don't know what is currently holding it up from going live.
Sep 26
parent reply Dominikus Dittes Scherkl <dominikus.scherkl continental-corporation.com> writes:
On Thursday, 26 September 2019 at 18:09:08 UTC, rikki cattermole 
wrote:

 We have a solution.

 https://github.com/dlang/DIPs/blob/master/DIPs/other/DIP1008.md

 I've looked at the dmd and Phobos PR's, I don't know what is 
 currently holding it up from going live.
I thought it's because RefCount doesn't work without the changes from the life proposal.
Sep 27
next sibling parent reply a11e99z <black80 bk.ru> writes:
On Friday, 27 September 2019 at 07:53:56 UTC, Dominikus Dittes 
Scherkl wrote:
 On Thursday, 26 September 2019 at 18:09:08 UTC, rikki 
 cattermole wrote:

 We have a solution.

 https://github.com/dlang/DIPs/blob/master/DIPs/other/DIP1008.md

 I've looked at the dmd and Phobos PR's, I don't know what is 
 currently holding it up from going live.
I thought it's because RefCount doesn't work without the changes from the life proposal.
what is life? (searching by forum gives only this message)
Sep 27
next sibling parent reply Paulo Pinto <pjmlp progtools.org> writes:
On Friday, 27 September 2019 at 09:36:33 UTC, a11e99z wrote:
 On Friday, 27 September 2019 at 07:53:56 UTC, Dominikus Dittes 
 Scherkl wrote:
 On Thursday, 26 September 2019 at 18:09:08 UTC, rikki 
 cattermole wrote:

 We have a solution.

 https://github.com/dlang/DIPs/blob/master/DIPs/other/DIP1008.md

 I've looked at the dmd and Phobos PR's, I don't know what is 
 currently holding it up from going live.
I thought it's because RefCount doesn't work without the changes from the life proposal.
what is life? (searching by forum gives only this message)
https://dlang.org/blog/2019/07/15/ownership-and-borrowing-in-d/
Sep 27
parent reply "H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
On Fri, Sep 27, 2019 at 09:55:27AM +0000, Paulo Pinto via Digitalmars-d wrote:
 On Friday, 27 September 2019 at 09:36:33 UTC, a11e99z wrote:
[...]
 what is  life? (searching by forum gives only this message)
https://dlang.org/blog/2019/07/15/ownership-and-borrowing-in-d/
Thanks for the link! I've also been wondering, with all this talk about live. One comment on the article, though: ownership as defined by Walter seems unnecessarily restrictive, in that it's impossible to have multiple mutable pointers to an object. Here's an enhancement based on an idea I had years ago: the concept of differentiating between "owning" pointers and "non-owning" pointers. An owning pointer is basically what Walter describes -- only a single such pointer can exist per object, and when that pointer goes out of the scope the object gets freed. Allocation functions like malloc() return an owning pointer. During its lifetime, however, any number of "non-owning" pointers can be made from it. A "non-owning" pointer cannot be passed to free() (or equivalent). In this scheme, free() only ever accepts an owning pointer. Furthermore, a non-owning pointer cannot outlive the scope of the owning pointer: they are 'scope', and their scope must be statically provable to be a subset of the scope of the owning pointer they are borrowing from. There is no need to restrict them with regards to mutability, however. This system can be implemented using RAII: pointers returned from memory allocators like malloc() are wrapped in an OwnerPtr struct that frees the pointer once it goes out of scope. It overrides assignment and copy ctors such that an OwnerPtr becomes null after it gets copied to another OwnerPtr (it has move semantics, and disables copy). An OwnerPtr can be copied into a regular pointer, under the condition that the regular pointer is scope (it cannot outlive the OwnerPtr itself). I used to actually use this scheme for memory management back when I was writing C/C++ code, and it works fairly well: only back then I didn't have 'scope' so there was the danger of a non-owning pointer leaking out and becoming a dangling pointer. With D's scope, this becomes a waterproof scheme AFAICT. T -- Never ascribe to malice that which is adequately explained by incompetence. -- Napoleon Bonaparte
Sep 27
parent reply Timon Gehr <timon.gehr gmx.ch> writes:
On 27.09.19 18:40, H. S. Teoh wrote:
 On Fri, Sep 27, 2019 at 09:55:27AM +0000, Paulo Pinto via Digitalmars-d wrote:
 On Friday, 27 September 2019 at 09:36:33 UTC, a11e99z wrote:
[...]
 what is  life? (searching by forum gives only this message)
https://dlang.org/blog/2019/07/15/ownership-and-borrowing-in-d/
Thanks for the link! I've also been wondering, with all this talk about live. One comment on the article, though: ownership as defined by Walter seems unnecessarily restrictive, in that it's impossible to have multiple mutable pointers to an object.
Not impossible, you are just not supposed to do it. The proposal does zero checking at the safe/ live interface, which means it's by convention. live in the best case does nothing at all and in the worst case rips holes into safe instead of closing them.
 Here's an enhancement based on an idea I
 had years ago: the concept of differentiating between "owning" pointers
 and "non-owning" pointers.
 ...
Yes, that's much better than a function attribute, but owning pointers don't need to be a built-in language concept. (See Rust.)
 An owning pointer is basically what Walter describes -- only a single
 such pointer can exist per object, and when that pointer goes out of the
 scope the object gets freed.  Allocation functions like malloc() return
 an owning pointer.  During its lifetime, however, any number of
 "non-owning" pointers can be made from it.
 ...
That's what's called borrowing in Rust, but you need to be careful.
 A "non-owning" pointer cannot be passed to free() (or equivalent).
Issue is you can have a non-owning pointer pointing to a data structure that contains owning pointers.
 In this scheme, free() only ever accepts an owning pointer.  Furthermore, a
 non-owning pointer cannot outlive the scope of the owning pointer: they
 are 'scope', and their scope must be statically provable to be a subset
 of the scope of the owning pointer they are borrowing from. There is no
 need to restrict them with regards to mutability, however.
 ...
Agreed.
 This system can be implemented using RAII: pointers returned from memory
 allocators like malloc() are wrapped in an OwnerPtr struct that frees
 the pointer once it goes out of scope. It overrides assignment and copy
 ctors such that an OwnerPtr becomes null after it gets copied to another
 OwnerPtr (it has move semantics, and disables copy).
 
 An OwnerPtr can be copied into a regular pointer, under the condition
 that the regular pointer is scope (it cannot outlive the OwnerPtr
 itself).
 ...
As mentioned above this works if you have only one level of indirection.
 I used to actually use this scheme for memory management back when I was
 writing C/C++ code, and it works fairly well: only back then I didn't
 have 'scope' so there was the danger of a non-owning pointer leaking out
 and becoming a dangling pointer.  With D's scope, this becomes a
 waterproof scheme AFAICT.
 
Not really. E.g., you can have two non-owning pointers to a Array!int and then one of them reassigns the array, while the other borrowed a pointer to one of its elements. Rust enforces no mutable aliasing exactly because reassigning something may cause the old value to be deallocated.
Sep 27
parent "H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
On Fri, Sep 27, 2019 at 07:13:36PM +0200, Timon Gehr via Digitalmars-d wrote:
[...]
 A "non-owning" pointer cannot be passed to free() (or equivalent).
Issue is you can have a non-owning pointer pointing to a data structure that contains owning pointers.
[...]
 [...] E.g., you can have two non-owning pointers to a Array!int and
 then one of them reassigns the array, while the other borrowed a
 pointer to one of its elements.
Could you give a specific example with pseudocode? I'm having trouble understanding exactly what goes wrong here. I got as far as: struct S { OwnerPtr!int value; } OwnerPtr!S s = allocate!S(); S* s1 = s; // borrow S* s2 = s; // borrow int* elem = s1.value; // borrow from contained owning ptr s2.value = new OwnerPtr!int(123); // invalidates elem Doesn't this just mean that non-ownership should be transitive? I.e., if you have a non-owning pointer to some data structure, then all OwnerPtr's in the data structure effectively become non-owned from your POV, so reassigning is illegal.
 Rust enforces no mutable aliasing exactly because reassigning
 something may cause the old value to be deallocated.
Doesn't that just mean that the borrowed pointer had a lifetime that exceeded the lifetime of the owning pointer? Therefore, it was (retroactively) illegal to have borrowed that pointer in the first place, since you couldn't guarantee that the owning pointer wouldn't become invalidated. Transitive non-ownership seems to be one way of dealing with this (though I'm not sure whether it's sufficient, or even consistent -- have to think about this more). T -- I am not young enough to know everything. -- Oscar Wilde
Sep 30
prev sibling parent Jonathan Marler <johnnymarler gmail.com> writes:
On Friday, 27 September 2019 at 09:36:33 UTC, a11e99z wrote:
 On Friday, 27 September 2019 at 07:53:56 UTC, Dominikus Dittes 
 Scherkl wrote:
 On Thursday, 26 September 2019 at 18:09:08 UTC, rikki 
 cattermole wrote:

 We have a solution.

 https://github.com/dlang/DIPs/blob/master/DIPs/other/DIP1008.md

 I've looked at the dmd and Phobos PR's, I don't know what is 
 currently holding it up from going live.
I thought it's because RefCount doesn't work without the changes from the life proposal.
what is life? (searching by forum gives only this message)
"Life explained as a video game" https://www.youtube.com/watch?v=ryFjCxGfqeE
Sep 27
prev sibling parent Timon Gehr <timon.gehr gmx.ch> writes:
On 27.09.19 09:53, Dominikus Dittes Scherkl wrote:
 On Thursday, 26 September 2019 at 18:09:08 UTC, rikki cattermole wrote:
 
 We have a solution.

 https://github.com/dlang/DIPs/blob/master/DIPs/other/DIP1008.md

 I've looked at the dmd and Phobos PR's, I don't know what is currently 
 holding it up from going live.
I thought it's because RefCount doesn't work without the changes from the life proposal.
(Nor with those changes.)
Sep 27
prev sibling next sibling parent reply Jonathan Marler <johnnymarler gmail.com> writes:
On Thursday, 26 September 2019 at 17:51:16 UTC, shfit wrote:
 Herb Sutter gave a pretty interesting talk on zero cost 
 exceptions and RTTI.

 https://m.youtube.com/watch?v=ARYP83yNAWk

 Could this be an option for D to avoid  gc exceptions?
Wow this is a really big deal. Zero-overhead exception with no memory allocation. Could be a big feature for D.
Sep 27
parent Ola Fosheim =?UTF-8?B?R3LDuHN0YWQ=?= <ola.fosheim.grostad gmail.com> writes:
On Friday, 27 September 2019 at 17:26:02 UTC, Jonathan Marler 
wrote:
 On Thursday, 26 September 2019 at 17:51:16 UTC, shfit wrote:
 Herb Sutter gave a pretty interesting talk on zero cost 
 exceptions and RTTI.

 https://m.youtube.com/watch?v=ARYP83yNAWk

 Could this be an option for D to avoid  gc exceptions?
Wow this is a really big deal. Zero-overhead exception with no memory allocation. Could be a big feature for D.
Well, IIRC modern C++ does not do memory allocation today, it uses a preallocated buffer. What he is talking about is replicating Rust-style "Optional" union return values as throw/catch but implemented in registers only. So it is basically about reducing the amount of state that is transferred to the caller from the exception location with some hints of the implementation. For D this will be difficult because D provides the exception chain. I suggested register based throwing for D many years ago. Nothing new...
Sep 27
prev sibling parent reply Manu <turkeyman gmail.com> writes:
On Thu, Sep 26, 2019 at 10:55 AM shfit via Digitalmars-d
<digitalmars-d puremagic.com> wrote:
 Herb Sutter gave a pretty interesting talk on zero cost
 exceptions and RTTI.

 https://m.youtube.com/watch?v=ARYP83yNAWk

 Could this be an option for D to avoid  gc exceptions?
If C++ does this, and we want to extern(C++), then we will support this... An interesting question might be if we are able to get ahead of the curve. I found his talk very motivating.
Sep 29
parent Johannes Pfau <nospam example.com> writes:
Am Sun, 29 Sep 2019 18:57:21 -0700 schrieb Manu:

 On Thu, Sep 26, 2019 at 10:55 AM shfit via Digitalmars-d
 <digitalmars-d puremagic.com> wrote:
 Herb Sutter gave a pretty interesting talk on zero cost exceptions and
 RTTI.

 https://m.youtube.com/watch?v=ARYP83yNAWk

 Could this be an option for D to avoid  gc exceptions?
If C++ does this, and we want to extern(C++), then we will support this... An interesting question might be if we are able to get ahead of the curve. I found his talk very motivating.
It's a very interesting idea. In the end, what he proposes is to bake support for error codes into the language, but in a way which resembles exception handling. I think reusing the return channel for error codes in a union-like way with the result value and a flag to discriminate is quite clever. However, compared to exceptions your still trading no CPU-overhead in the success case and lots of overhead in the failure case for a little overhead in success and a little overhead in failure (the compiler always has to check the flag after calling such a function, to propagate the error further). But I'd think this is not a large problem: Performance sensitive code probably doesn't use exceptions anyway, cause in most cases where you can't afford this little overhead in the success case, you probably can't afford the large overhead exceptions cause in the failure case either. But still, we'd need some kind of benchmarks in the end. I also like this idea a lot cause it makes exception-like error handling viable for betterC/ microcontroller environments. So for his idea 1, implementing error value in a return channel: We should really experiment with that in D. We also have the benefit of already having nothrow... For his idea 2&3 IIRC, we already have contracts and IIRC we already have a flag which simply aborts the application / calls the debugger on failure instead of throwing. Regarding handling / not handling OOM to make more code nothrow: Seems interesting, but I'd have to think about it more, how it relates to D. His idea 4, explicitly marking all calls which can throw in some way (try), seems to be something which should be easy to implement in D. It's probably 99% compatible with the current grammar, we just need to allow try without catch. (Not sure if we need try at expression level as well: `bool a = try foo();` vs `try bool a = foo();`) But it needs a DIP of course. I'm not sure about the RTTI part: The downcast optimization is clever, but it only works if you can do whole-program-optimization (i.e. statically linked). Of course in embedded, this is the default case, so it is important and useful. Still, you should also be able to just walk up vtables in a linked-list style. Then all RTTI you need is one field in the Vtable which always links to the parent vtable, and when casting, you do this: class Foo{} clas Bar: Foo{} cast(Bar)foo => auto vtbl = foo.vtbl; while (vtbl && vtbl != Bar.vtbl) vtbl = vtbl.parent(); if (vtbl) // cast sucessfull else // cast failed Which has very little overhead. The main drawback for hard-realtime is that the overhead is not bounded: It's O(n) where n is the inheritance depth of the runtime object (foo). In practice, you're probably walking 1-20 elements in a linked list, which should be fast. But you can't easily determine n statically, unless you know all possible types for foo. (C++ probably can't do this because of multiple inheritance. Thinking about this, we probably also get problems when we want to cast to parent interfaces this way...) -- Johannes
Sep 29