digitalmars.D - Truly nogc Exceptions?
- Steven Schveighoffer (151/151) Sep 19 2018 Given dip1008, we now can throw exceptions inside @nogc code! This is
- Steven Schveighoffer (3/8) Sep 19 2018 Aand, no we can't. Because the sink could actually allocate.
- Seb (7/10) Sep 19 2018 We recently introduced support for output ranges in the
- Steven Schveighoffer (10/23) Sep 19 2018 I don't think that will work here, as Throwable is a class.
-
Atila Neves
(13/22)
Sep 20 2018
- Steven Schveighoffer (10/35) Sep 20 2018 I thought it already did that? How is the exception destroyed when
- Atila Neves (8/36) Sep 20 2018 I've had a 2 week holiday since I was working on this so I don't
- H. S. Teoh (14/20) Sep 20 2018 [...]
- Steven Schveighoffer (19/38) Sep 20 2018 Except we DON'T construct the stack trace string, even lazily. If you
- H. S. Teoh (11/31) Sep 20 2018 [...]
- Neia Neutuladh (5/10) Sep 20 2018 I believe Tango did this a decade ago. It's a solid strategy.
- Atila Neves (34/71) Oct 19 2018 Nope:
- Nicholas Wilson (3/8) Oct 19 2018 Wow, that is useless! Is that an implementation bug or was that
- Atila Neves (3/13) Oct 19 2018 I hope it's a bug - I opened an issue:
- welkam (12/15) Oct 21 2018 If I understood you correctly then we have same idea. If
- Adam D. Ruppe (23/24) Sep 20 2018 Nah, I agree. Actually, I'm of the opinion that string error
- Steven Schveighoffer (10/39) Sep 20 2018 Awesome! This is just what I was thinking of. In fact, I did something
- Adam D. Ruppe (8/13) Sep 20 2018 Aye. Note that RangeError is called by the compiler though, so
- Steven Schveighoffer (7/14) Sep 20 2018 Well, you can always override that. Just do the check yourself and throw...
- Adam D. Ruppe (34/36) Sep 20 2018 Walter's objection was code size - it would throw stuff out of
- Steven Schveighoffer (7/52) Sep 20 2018 Sounds like a case of premature optimization at best.
- Paolo Invernizzi (6/23) Oct 20 2018 If `@nogc` could be relaxed for `new Error` exactly for that
- Mike Parker (3/6) Oct 20 2018 https://github.com/dlang/DIPs/blob/master/DIPs/DIP1008.md
- Paolo Invernizzi (3/12) Oct 20 2018 Yep, you are right, of course... 8-/
- Jonathan M Davis (23/26) Sep 21 2018 I pointed out this problem when the DIP was orginally proposed and have
- Nemanja Boric (13/18) Sep 21 2018 The @__future is fully (to a reasonable degree) implemented - and
- Nemanja Boric (2/15) Sep 21 2018
- Petar Kirov [ZombineDev] (9/27) Sep 21 2018 If in theory D had all the memory-safety features to make this
Given dip1008, we now can throw exceptions inside nogc code! This is really cool, and helps make code that uses exceptions or errors nogc. Except... The mechanism to report what actually went wrong for an exception is a string passed to the exception during *construction*. Given that you likely want to make such an exception inside a nogc function, you are limited to passing a compile-time-generated string (either a literal or one generated via CTFE). To demonstrate what I mean, let me give you an example member function inside a type containing 2 fields, x and y: void foo(int[] arr) { auto x = arr[x .. y]; } There are 2 ways this can throw a range error: a) x > y b) y > arr.length But which is it? And what are x and y, or even the array length? The error message we get is basic (module name and line number aren't important here): core.exception.RangeError testerror.d(6): Range violation Not good enough -- we have all the information present to give a more detailed message. Why not: Attempted slice with wrong ordered parameters, 5 .. 4 or Slice parameter 6 is greater than length 5 All that information is available, yet we don't see anything like that. Let's look at the base of all exception and error types to see why we don't have such a thing. The part which prints this message is the member function toString inside Throwable, repeated here for your reading pleasure [1]: void toString(scope void delegate(in char[]) sink) const { import core.internal.string : unsignedToTempString; char[20] tmpBuff = void; sink(typeid(this).name); sink(" "); sink(file); sink("("); sink(unsignedToTempString(line, tmpBuff, 10)); sink(")"); if (msg.length) { sink(": "); sink(msg); } if (info) { try { sink("\n----------------"); foreach (t; info) { sink("\n"); sink(t); } } catch (Throwable) { // ignore more errors } } } (Side Note: there is an overload for toString which takes no delegate and returns a string. But since this overload is present, doing e.g. writeln(myEx) will use it) Note how this *doesn't* allocate anything. But hang on, what about the part that actually prints the message: sink(typeid(this).name); sink(" "); sink(file); sink("("); sink(unsignedToTempString(line, tmpBuff, 10)); sink(")"); if (msg.length) { sink(": "); sink(msg); } Hm... Note how the file name, and the line number are all *members* of the exception, and there was no need to allocate a special string to contain the message we saw. So it *is* possible to have a custom message without allocation. It's just that the only interface for details is via the `msg` string member field -- which is only set on construction. We can do better. I noticed that there is a __future member function inside Throwable called message. This function returns the message that the Throwable is supposed to display (defaulting to return msg). I believe this was inserted at Sociomantic's request, because they need to be able to have a custom message rendered at *print* time, not *construction* time [2]. This makes sense -- why do we need to allocate some string that will never be printed (in the case where an exception is caught and handled)? This helps alleviate the problem a bit, as we could construct our message at print-time when the nogc requirement is no longer present. But we can do even better. What if we added ALSO a function: void message(scope void delegate(in char[]) sink) In essence, this does *exactly* what the const(char)[] returning form of message does, but it doesn't require any allocation, nor storage of the data to print inside the exception. We can print numbers (and other things) and combine them together with strings just like the toString function does. We can then replace the code for printing the message inside toString with this: bool printedColon = false; void subSink(in char[] data) { if(!printedColon && data.length > 0) { sink(": "); printedColon = true; } sink(data); } message(&subSink); In this case, we then have a MUCH better mechanism to implement our desired output from the slice error: class RangeSliceError : Throwable { size_t lower; size_t upper; size_t len; ... override void message(scope void delegate(in char[]) sink) { import core.internal.string : unsignedToTempString; char[20] tmpBuff = void; if (lower > upper) { sink("Attempted slice with wrong ordered parameters "); sink(unsignedToTempString(lower, tmpBuff, 10)); sink(" .. "); sink(unsignedToTempString(upper, tmpBuff, 10)); } else if (upper > len) { sink("Slice parameter "); sink(unsignedToTempString(upper, tmpBuff, 10)); sink(" is greater than length "); sink(unsignedToTempString(len, tmpBuff, 10)); } else // invalid parameters to this class sink("Slicing Error, but unsure why"); } } And look Ma, no allocations! So we can truly have nogc exceptions, without having requirements to use the GC on construction. I think we should add this. One further thing: I didn't make the sink version of message nogc, but in actuality, it could be. Notice how it allocates using the stack. Even if we needed some indeterminate amount of memory, it would be simple to use C malloc/free, or alloca. But traditionally, we don't put any attributes on these base functions. Would it make sense in this case? As Andrei says -- Destroy! -Steve [1] - https://github.com/dlang/druntime/blob/542b680f2c2e09e7f4b494898437c61216583fa5/src/object.d#L2642 [2] - https://github.com/dlang/druntime/pull/1895
Sep 19 2018
On 9/19/18 5:16 PM, Steven Schveighoffer wrote:One further thing: I didn't make the sink version of message nogc, but in actuality, it could be. Notice how it allocates using the stack. Even if we needed some indeterminate amount of memory, it would be simple to use C malloc/free, or alloca. But traditionally, we don't put any attributes on these base functions. Would it make sense in this case?Aand, no we can't. Because the sink could actually allocate. -Steve
Sep 19 2018
On Wednesday, 19 September 2018 at 21:28:56 UTC, Steven Schveighoffer wrote:On 9/19/18 5:16 PM, Steven Schveighoffer wrote:We recently introduced support for output ranges in the formatting of Phobos: https://dlang.org/changelog/2.079.0.html#toString Output ranges have the advantage that they could be nogc and because of the templatization also safe.One further thing: I didn't make the sink version of message nogc, but in actuality, it could be.
Sep 19 2018
On 9/19/18 7:53 PM, Seb wrote:On Wednesday, 19 September 2018 at 21:28:56 UTC, Steven Schveighoffer wrote:I don't think that will work here, as Throwable is a class. All I can think of is that you would have 2 versions, a nogc one that takes a nogc delegate, and one that is not. Of course, your exception type could define something completely separate, and you can deal with it in your own project as needed. If there was a way to say "this is nogc if you give it a nogc delegate, and not if you don't", that would be useful. The compiler could verify it at compile-time. -SteveOn 9/19/18 5:16 PM, Steven Schveighoffer wrote:We recently introduced support for output ranges in the formatting of Phobos: https://dlang.org/changelog/2.079.0.html#toString Output ranges have the advantage that they could be nogc and because of the templatization also safe.One further thing: I didn't make the sink version of message nogc, but in actuality, it could be.
Sep 19 2018
On Wednesday, 19 September 2018 at 21:16:00 UTC, Steven Schveighoffer wrote:Given dip1008, we now can throw exceptions inside nogc code! This is really cool, and helps make code that uses exceptions or errors nogc. Except... The mechanism to report what actually went wrong for an exception is a string passed to the exception during *construction*. Given that you likely want to make such an exception inside a nogc function, you are limited to passing a compile-time-generated string (either a literal or one generated via CTFE).<snip> I expressed my concern for DIP1008 and the `msg` field when it was first announced. I think the fix is easy and a one line change to dmd. I also expressed this on that thread but was apparently ignored. What's the fix? Have the compiler insert a call to the exception's destructor at the end of the `catch(scope Exception)` block. That's it. The `msg` field is just a slice, point it to RAII managed memory and you're good to go. Give me deterministic destruction of exceptions caught by scope when using dip1008 and I'll give you nogc exception throwing immediately. I've even already written the code!
Sep 20 2018
On 9/20/18 6:48 AM, Atila Neves wrote:On Wednesday, 19 September 2018 at 21:16:00 UTC, Steven Schveighoffer wrote:I thought it already did that? How is the exception destroyed when dip1008 is enabled? But this means you still have to build msg when throwing the error/exception. It's not needed until you print it, and there's no reason anyway to make it allocate, even with RAII. For some reason D forces msg to be built, but it does't e.g. build the entire stack trace string before hand, or build the string that shows the exception class name or the file/line beforehand. -SteveGiven dip1008, we now can throw exceptions inside nogc code! This is really cool, and helps make code that uses exceptions or errors nogc. Except... The mechanism to report what actually went wrong for an exception is a string passed to the exception during *construction*. Given that you likely want to make such an exception inside a nogc function, you are limited to passing a compile-time-generated string (either a literal or one generated via CTFE).<snip> I expressed my concern for DIP1008 and the `msg` field when it was first announced. I think the fix is easy and a one line change to dmd. I also expressed this on that thread but was apparently ignored. What's the fix? Have the compiler insert a call to the exception's destructor at the end of the `catch(scope Exception)` block. That's it. The `msg` field is just a slice, point it to RAII managed memory and you're good to go. Give me deterministic destruction of exceptions caught by scope when using dip1008 and I'll give you nogc exception throwing immediately. I've even already written the code!
Sep 20 2018
On Thursday, 20 September 2018 at 12:48:13 UTC, Steven Schveighoffer wrote:On 9/20/18 6:48 AM, Atila Neves wrote:I've had a 2 week holiday since I was working on this so I don't quite remember. I'll try and find out the specifics for you later. What I do remember is that the destructor didn't get called.On Wednesday, 19 September 2018 at 21:16:00 UTC, Steven Schveighoffer wrote:I thought it already did that? How is the exception destroyed when dip1008 is enabled?[...]<snip> I expressed my concern for DIP1008 and the `msg` field when it was first announced. I think the fix is easy and a one line change to dmd. I also expressed this on that thread but was apparently ignored. What's the fix? Have the compiler insert a call to the exception's destructor at the end of the `catch(scope Exception)` block. That's it. The `msg` field is just a slice, point it to RAII managed memory and you're good to go. Give me deterministic destruction of exceptions caught by scope when using dip1008 and I'll give you nogc exception throwing immediately. I've even already written the code!But this means you still have to build msg when throwing the error/exception. It's not needed until you print it, and there's no reason anyway to make it allocate, even with RAII.Maybe, but it's a lot easier to just allocate and have the destructor take care of it.For some reason D forces msg to be built, but it does't e.g. build the entire stack trace string before hand, or build the string that shows the exception class name or the file/line beforehand.Good points.
Sep 20 2018
On Thu, Sep 20, 2018 at 08:48:13AM -0400, Steven Schveighoffer via Digitalmars-d wrote: [...]But this means you still have to build msg when throwing the error/exception. It's not needed until you print it, and there's no reason anyway to make it allocate, even with RAII. For some reason D forces msg to be built, but it does't e.g. build the entire stack trace string before hand, or build the string that shows the exception class name or the file/line beforehand.[...] IIRC, originally the stacktrace was also built at exception construction time. But it was causing a major performance hit, so eventually someone changed the code to construct it lazily (i.e., only when the catcher actually tries to look it up). I think it makes sense to also make .msg lazy, if the exception object is already carrying enough info to build the message when the catcher asks for it. And if the catcher doesn't ask for it, we saved an extra GC allocation (which is a plus even if we're not trying to go nogc). T -- Computers shouldn't beep through the keyhole.
Sep 20 2018
On 9/20/18 11:06 AM, H. S. Teoh wrote:On Thu, Sep 20, 2018 at 08:48:13AM -0400, Steven Schveighoffer via Digitalmars-d wrote: [...]Except we DON'T construct the stack trace string, even lazily. If you look at the code I posted, it's output directly to the output buffer (via the sink delegate), without ever having allocated. I think we can do that for the message too (why not, it's all supported). But either one (using GC at print time, or lazily outputting to buffer at print time) solves the biggest problem -- being able to construct an exception without the GC. Plus, this nudges developers of exceptions to store more useful data. If you catch an exception that has details in it, possibly it is only going to be in the string, which you now have to *parse* to get out what the problem was. If instead it was standard practice just to store the details, and then construct the string later, more useful information would be available in the form of fields/accessors. Think about this -- every ErrnoException that is thrown allocates its message via the GC on construction. Even if you catch that and just look at the errno code. Even with dip1008: https://github.com/dlang/phobos/blob/6a15dfbe18f9151379f6337f53a3c41d12dee939/std/exception.d#L1625 -SteveBut this means you still have to build msg when throwing the error/exception. It's not needed until you print it, and there's no reason anyway to make it allocate, even with RAII. For some reason D forces msg to be built, but it does't e.g. build the entire stack trace string before hand, or build the string that shows the exception class name or the file/line beforehand.[...] IIRC, originally the stacktrace was also built at exception construction time. But it was causing a major performance hit, so eventually someone changed the code to construct it lazily (i.e., only when the catcher actually tries to look it up). I think it makes sense to also make .msg lazy, if the exception object is already carrying enough info to build the message when the catcher asks for it. And if the catcher doesn't ask for it, we saved an extra GC allocation (which is a plus even if we're not trying to go nogc).
Sep 20 2018
On Thu, Sep 20, 2018 at 11:25:27AM -0400, Steven Schveighoffer via Digitalmars-d wrote:On 9/20/18 11:06 AM, H. S. Teoh wrote:[...][...] Yeah, that's what I meant. :D Well, for backward compatibility we could still have .msg allocate and return a string, but we could provide an overload / alternate member function that writes directly to a sink instead. Then we don't ever have to allocate unless the sink itself does. T -- I am a consultant. My job is to make your job redundant. -- Mr TomIIRC, originally the stacktrace was also built at exception construction time. But it was causing a major performance hit, so eventually someone changed the code to construct it lazily (i.e., only when the catcher actually tries to look it up). I think it makes sense to also make .msg lazy, if the exception object is already carrying enough info to build the message when the catcher asks for it. And if the catcher doesn't ask for it, we saved an extra GC allocation (which is a plus even if we're not trying to go nogc).Except we DON'T construct the stack trace string, even lazily. If you look at the code I posted, it's output directly to the output buffer (via the sink delegate), without ever having allocated. I think we can do that for the message too (why not, it's all supported). But either one (using GC at print time, or lazily outputting to buffer at print time) solves the biggest problem -- being able to construct an exception without the GC.
Sep 20 2018
On Thursday, 20 September 2018 at 15:52:15 UTC, H. S. Teoh wrote:Yeah, that's what I meant. :D Well, for backward compatibility we could still have .msg allocate and return a string, but we could provide an overload / alternate member function that writes directly to a sink instead. Then we don't ever have to allocate unless the sink itself does.I believe Tango did this a decade ago. It's a solid strategy. However, with Tango, the default was to implement the toString(sink) function by calling the regular toString(), which was quite convenient but won't work with nogc.
Sep 20 2018
On Thursday, 20 September 2018 at 12:48:13 UTC, Steven Schveighoffer wrote:On 9/20/18 6:48 AM, Atila Neves wrote:Nope: --------------- class MyException: Exception { static int numInstances; this(string msg) { super(msg); ++numInstances; } ~this() { --numInstances; } } void main() { assert(MyException.numInstances == 0); try throw new MyException("oops"); catch(MyException _) assert(MyException.numInstances == 1); assert(MyException.numInstances == 0); } --------------- % dmd -dip1008 -run exception.d core.exception.AssertError exception.d(21): Assertion failureOn Wednesday, 19 September 2018 at 21:16:00 UTC, Steven Schveighoffer wrote:I thought it already did that?Given dip1008, we now can throw exceptions inside nogc code! This is really cool, and helps make code that uses exceptions or errors nogc. Except... The mechanism to report what actually went wrong for an exception is a string passed to the exception during *construction*. Given that you likely want to make such an exception inside a nogc function, you are limited to passing a compile-time-generated string (either a literal or one generated via CTFE).<snip> I expressed my concern for DIP1008 and the `msg` field when it was first announced. I think the fix is easy and a one line change to dmd. I also expressed this on that thread but was apparently ignored. What's the fix? Have the compiler insert a call to the exception's destructor at the end of the `catch(scope Exception)` block. That's it. The `msg` field is just a slice, point it to RAII managed memory and you're good to go. Give me deterministic destruction of exceptions caught by scope when using dip1008 and I'll give you nogc exception throwing immediately. I've even already written the code!How is the exception destroyed when dip1008 is enabled?Apparently, it isn't. Which renders dip1008 pretty much useless since we could already use static immutable exceptions before.But this means you still have to build msg when throwing the error/exception. It's not needed until you print it, and there's no reason anyway to make it allocate, even with RAII. For some reason D forces msg to be built, but it does't e.g. build the entire stack trace string before hand, or build the string that shows the exception class name or the file/line beforehand.Allocating and building the string doesn't bother me - in all of my uses it's eventually going to get printed (which means the string needed to be built), and the exceptional path can be slow, I don't mind. But, one could always store a tuple of members in an exception class instead and only build the string on demand. I just think it's easier with an RAII string.
Oct 19 2018
On Friday, 19 October 2018 at 23:34:01 UTC, Atila Neves wrote:On Thursday, 20 September 2018 at 12:48:13 UTC, Steven Schveighoffer wrote:Wow, that is useless! Is that an implementation bug or was that specified by the DIP?How is the exception destroyed when dip1008 is enabled?Apparently, it isn't. Which renders dip1008 pretty much useless since we could already use static immutable exceptions before.
Oct 19 2018
On Friday, 19 October 2018 at 23:46:29 UTC, Nicholas Wilson wrote:On Friday, 19 October 2018 at 23:34:01 UTC, Atila Neves wrote:I hope it's a bug - I opened an issue: https://issues.dlang.org/show_bug.cgi?id=19317On Thursday, 20 September 2018 at 12:48:13 UTC, Steven Schveighoffer wrote:Wow, that is useless! Is that an implementation bug or was that specified by the DIP?How is the exception destroyed when dip1008 is enabled?Apparently, it isn't. Which renders dip1008 pretty much useless since we could already use static immutable exceptions before.
Oct 19 2018
On Thursday, 20 September 2018 at 10:48:35 UTC, Atila Neves wrote:What's the fix? Have the compiler insert a call to the exception's destructor at the end of the `catch(scope Exception)` block.If I understood you correctly then we have same idea. If exception is not handled or re thrown then scope "exits" trough scope(failure) in which case no deallocation should be performed and only if scope "exits" trough scope(success), meaning exception/s were handled, destructors need to be called. To make exceptions nogc we need destructors that are aware of how scope was exited like: ~ this(exit) ( ) {} ~ this(success) ( ) {} ~ this(failure) ( ) {} With this its easy to make all exceptions nogc
Oct 21 2018
On Wednesday, 19 September 2018 at 21:16:00 UTC, Steven Schveighoffer wrote:As Andrei says -- Destroy!Nah, I agree. Actually, I'm of the opinion that string error messages in exceptions ought to be considered harmful: you shouldn't be doing strings at all. All the useful information should be in the type - the class name and the members with details. Well, defining a new class can sometimes be a mild hassle... but for really common ones, we really should just do it, and other ones can be done as templated classes or templated factory functions that define a new class right there and then. http://arsdnet.net/dcode/exception.d That's the proof-of-concept I wrote for this years ago, go to the bottom of the file for the usage example. It uses a reflection mixin to make writing the new classes easy, and I even wrote an enforce thing that can add more info by creating a subclass that stores arguments to functions so it can print it all (assuming they are sane to copy like strings or value things lol) enforce!fopen("nofile.txt".ptr, "rb".ptr); MyExceptionBase exception.d(38): fopen call failed filename = nofile.txt mode = rb ----------------
Sep 20 2018
On 9/20/18 11:33 AM, Adam D. Ruppe wrote:On Wednesday, 19 September 2018 at 21:16:00 UTC, Steven Schveighoffer wrote:Awesome! This is just what I was thinking of. In fact, I did something similar locally since I needed to know what the slice parameters that were failing were. I still had to trick the nogc to get around the "new Exception" piece. The printMembers thing is nice. I think for druntime/phobos, however, we should have a base that just calls a virtual function with the idea that the message is printed, and then a further-derived type could do the printMembers thing if that's what you want. -SteveAs Andrei says -- Destroy!Nah, I agree. Actually, I'm of the opinion that string error messages in exceptions ought to be considered harmful: you shouldn't be doing strings at all. All the useful information should be in the type - the class name and the members with details. Well, defining a new class can sometimes be a mild hassle... but for really common ones, we really should just do it, and other ones can be done as templated classes or templated factory functions that define a new class right there and then. http://arsdnet.net/dcode/exception.d That's the proof-of-concept I wrote for this years ago, go to the bottom of the file for the usage example. It uses a reflection mixin to make writing the new classes easy, and I even wrote an enforce thing that can add more info by creating a subclass that stores arguments to functions so it can print it all (assuming they are sane to copy like strings or value things lol) enforce!fopen("nofile.txt".ptr, "rb".ptr); MyExceptionBase exception.d(38): fopen call failed filename = nofile.txt mode = rb ----------------
Sep 20 2018
On Thursday, 20 September 2018 at 15:52:03 UTC, Steven Schveighoffer wrote:I needed to know what the slice parameters that were failing were.Aye. Note that RangeError is called by the compiler though, so you gotta patch dmd to make it pass the arguments to it for index. Ugh. I did a PR for this once but it got shot down because of an allegeded (without evidence btw) performance degradation. Ugh.The printMembers thing is nice. I think for druntime/phobos, however, we should have a base that just calls a virtual function with the idea that the message is printedAye.
Sep 20 2018
On 9/20/18 12:24 PM, Adam D. Ruppe wrote:On Thursday, 20 September 2018 at 15:52:03 UTC, Steven Schveighoffer wrote:Well, you can always override that. Just do the check yourself and throw the error you want ;) In my case, that's what I did anyway. I don't know how a performance problem can occur on an error being thrown anyway -- the process is about to end. -SteveI needed to know what the slice parameters that were failing were.Aye. Note that RangeError is called by the compiler though, so you gotta patch dmd to make it pass the arguments to it for index. Ugh. I did a PR for this once but it got shot down because of an allegeded (without evidence btw) performance degradation. Ugh.
Sep 20 2018
On Thursday, 20 September 2018 at 17:14:12 UTC, Steven Schveighoffer wrote:I don't know how a performance problem can occur on an error being thrown anyway -- the process is about to end.Walter's objection was code size - it would throw stuff out of cache lines, even if it doesn't need to actually run. So like this line: int[] a; a = a[1 .. $]; With no bounds checking is just inc a.ptr; but with bounds checking it becomes something more like mov ecx, a.length cmp ecx, 1 // in other words, if length >= offset jae proceed push line push file call d_arraybounds // throws the error proceed: inc a.ptr Now, what my patch did was just, right before push line, it inserted "push length; push offset;". I believe this to be trivial since they are already loaded in registers or immediates and thus just a couple bytes for those instructions, but Walter (as I recall, this was a while ago and I didn't look up his exact words when writing this) said even a couple bytes are important for such a common operation as it throws off the L1 caches. I never got around to actually measuring the performance impact to prove one way or another. But... even if that is a problem, dmd -O will usually rearrange that to avoid the jump on the in-bounds case, and I'm sure ldc/gdc do too, sooooo the extra pushes' instruction bytes are off the main execution path anyway and thus shouldn't waste cache space. idk though. regardless, to me, the extra info is *well* worth the cost anyway.
Sep 20 2018
On 9/20/18 1:58 PM, Adam D. Ruppe wrote:On Thursday, 20 September 2018 at 17:14:12 UTC, Steven Schveighoffer wrote:Sounds like a case of premature optimization at best. Besides, if it is a performance issue, you aren't doing bounds checks on every slice/index anyway. I know in iopipe, to squeeze out every bit of performance, I avoid bounds checks when I know from previous asserts the bounds are correct. -SteveI don't know how a performance problem can occur on an error being thrown anyway -- the process is about to end.Walter's objection was code size - it would throw stuff out of cache lines, even if it doesn't need to actually run. So like this line: int[] a; a = a[1 .. $]; With no bounds checking is just inc a.ptr; but with bounds checking it becomes something more like mov ecx, a.length cmp ecx, 1 // in other words, if length >= offset jae proceed push line push file call d_arraybounds // throws the error proceed: inc a.ptr Now, what my patch did was just, right before push line, it inserted "push length; push offset;". I believe this to be trivial since they are already loaded in registers or immediates and thus just a couple bytes for those instructions, but Walter (as I recall, this was a while ago and I didn't look up his exact words when writing this) said even a couple bytes are important for such a common operation as it throws off the L1 caches. I never got around to actually measuring the performance impact to prove one way or another. But... even if that is a problem, dmd -O will usually rearrange that to avoid the jump on the in-bounds case, and I'm sure ldc/gdc do too, sooooo the extra pushes' instruction bytes are off the main execution path anyway and thus shouldn't waste cache space. idk though. regardless, to me, the extra info is *well* worth the cost anyway.
Sep 20 2018
On Thursday, 20 September 2018 at 17:14:12 UTC, Steven Schveighoffer wrote:On 9/20/18 12:24 PM, Adam D. Ruppe wrote:If ` nogc` could be relaxed for `new Error` exactly for that reason, pieces of Phobos could be turned ` nogc`... But I admit that that change would be controversial... - PaoloOn Thursday, 20 September 2018 at 15:52:03 UTC, Steven Schveighoffer wrote:Well, you can always override that. Just do the check yourself and throw the error you want ;) In my case, that's what I did anyway. I don't know how a performance problem can occur on an error being thrown anyway -- the process is about to end. -SteveI needed to know what the slice parameters that were failing were.Aye. Note that RangeError is called by the compiler though, so you gotta patch dmd to make it pass the arguments to it for index. Ugh. I did a PR for this once but it got shot down because of an allegeded (without evidence btw) performance degradation. Ugh.
Oct 20 2018
On Saturday, 20 October 2018 at 13:48:32 UTC, Paolo Invernizzi wrote:If ` nogc` could be relaxed for `new Error` exactly for that reason, pieces of Phobos could be turned ` nogc`... But I admit that that change would be controversial...https://github.com/dlang/DIPs/blob/master/DIPs/DIP1008.md
Oct 20 2018
On Saturday, 20 October 2018 at 14:56:37 UTC, Mike Parker wrote:On Saturday, 20 October 2018 at 13:48:32 UTC, Paolo Invernizzi wrote:Yep, you are right, of course... 8-/ /PIf ` nogc` could be relaxed for `new Error` exactly for that reason, pieces of Phobos could be turned ` nogc`... But I admit that that change would be controversial...https://github.com/dlang/DIPs/blob/master/DIPs/DIP1008.md
Oct 20 2018
On Wednesday, September 19, 2018 3:16:00 PM MDT Steven Schveighoffer via Digitalmars-d wrote:Given dip1008, we now can throw exceptions inside nogc code! This is really cool, and helps make code that uses exceptions or errors nogc. Except...I pointed out this problem when the DIP was orginally proposed and have always thought that it was kind of pointless because of this issue. The core problem is that Exception predates nogc. It predates ranges. Its design makes sense if exceptions are rare, and you're willing to use the GC. IMHO, it even makes sense in most cases with most code that is trying to avoid allocations, because the allocations are only going to occur when an error condition occurs. So, for most code bases, they won't affect performance. The problem is that even though that usually makes perfect sense, if you've written your code so that the non-error path doesn't allocate, you'd really like to be able to mark it with nogc, and you can't because of the exceptions. The DIP helps but not enough to really matter. And of course, in those code bases where you actually can't afford to have the error path allocate, it's even more of a problem. So, what we have is an Exception class that works perfectly well in a world without nogc but which doesn't work with nogc worth anything. And if we want to fix that, I think that we need to figure out how to fix Exception in a way that we can sanely transitition to whatever the new way we handle exception messages would be. I think that the message member was added by Dicebot as an attempt to fix this issue, because Sociomantic needed it, but I don't know exactly what's going on with that or __future. - Jonathan M Davis
Sep 21 2018
On Friday, 21 September 2018 at 09:10:06 UTC, Jonathan M Davis wrote:[...] I think that the message member was added by Dicebot as an attempt to fix this issue, because Sociomantic needed it, but I don't know exactly what's going on with that or __future. - Jonathan M DavisThe __future is fully (to a reasonable degree) implemented - and the `Throwable.message` was marked with this attribute to prevent breaking the user code when introducing this field, and it probably can just be removed from there at this point (since many releases have been released). This is the PR when Throwable.message was first introduced: https://github.com/dlang/druntime/pull/1445 We had the same discussion there, and you can see sociomantic's use case in Mihail's comments there. The entire PR have a very good discussion and we (not Sociomantic) still need sink-based method IIUC.
Sep 21 2018
On Friday, 21 September 2018 at 10:06:06 UTC, Nemanja Boric wrote:On Friday, 21 September 2018 at 09:10:06 UTC, Jonathan M Davis wrote:Interesting quote from Martin on that PR:[...]The __future is fully (to a reasonable degree) implemented - and the `Throwable.message` was marked with this attribute to prevent breaking the user code when introducing this field, and it probably can just be removed from there at this point (since many releases have been released). [...]With regards to Throwable.message we agreed on making it one of the first users of an upcoming reference counted string. Details on the design of the reference counted string first to be discussed on dlang-study.
Sep 21 2018
On Friday, 21 September 2018 at 11:48:50 UTC, Nemanja Boric wrote:On Friday, 21 September 2018 at 10:06:06 UTC, Nemanja Boric wrote:If in theory D had all the memory-safety features to make this work (think what languages like Rust have with linear/affine types [0] [1]) along with the necessary library implementations of smart pointers, RC-aware slicing, etc..., I would still prefer the sink-based approach in this situation, as it's much more elegant in my opinion. [0]: http://wiki.c2.com/?LinearTypes [1]: https://www.tweag.io/posts/2017-03-13-linear-types.htmlOn Friday, 21 September 2018 at 09:10:06 UTC, Jonathan M Davis wrote:Interesting quote from Martin on that PR:[...]The __future is fully (to a reasonable degree) implemented - and the `Throwable.message` was marked with this attribute to prevent breaking the user code when introducing this field, and it probably can just be removed from there at this point (since many releases have been released). [...]With regards to Throwable.message we agreed on making it one of the first users of an upcoming reference counted string. Details on the design of the reference counted string first to be discussed on dlang-study.
Sep 21 2018