February 09, 2014 Re: List of Phobos functions that allocate memory? | ||||
---|---|---|---|---|
| ||||
Posted in reply to Dmitry Olshansky | On 2/8/2014 2:11 PM, Dmitry Olshansky wrote:
> 09-Feb-2014 02:03, Walter Bright пишет:
>> On 2/7/2014 9:06 AM, Dmitry Olshansky wrote:
>>> Why throwing a single exception is such a big problem?
>>
>> Because in order to unwind the stack, you need to find the information
>> about the stack layout. This lookup is rather slow. You can make the
>> lookup faster by compromising the function code generation, but this is
>> considered an unacceptable tradeoff.
>
> A special table lookup can't be slow compared to writing a dummy HTTP 500
> response. Just saying. Yes, it's a tad slower then cmp + jz, I do understand that.
>
> Again I'm trying to say that framing stack unwinding as the culprit of vibe.d
> crawling under bad requests is plain wrong, and that was the focal point of the
> original argument.
I don't know how vibe.d works, but my point is using exception handling to implement normal control flow is bad design and it is going to be slow and the reason it is slow is because of the table lookup and unwinding cost, and that is not going to be fixed.
|
February 09, 2014 Re: List of Phobos functions that allocate memory? | ||||
---|---|---|---|---|
| ||||
Posted in reply to Ola Fosheim Grøstad | On 2/8/2014 2:59 PM, "Ola Fosheim Grøstad" <ola.fosheim.grostad+dlang@gmail.com>" wrote: > On Saturday, 8 February 2014 at 22:03:13 UTC, Walter Bright wrote: >> You can make the lookup faster by compromising the function code generation, >> but this is considered an unacceptable tradeoff. > > "Compromising"? You mean they had to modify codegen, which they didn't want to. > Clearly, if you know the return address you also could have stack info access > close to it (at a fixed offset), at no runtime cost whatsoever. Ola, I've done it both ways, I actually do know what I'm talking about. I've sometimes been proven wrong here, so you're welcome to do a pull request proving so. |
February 09, 2014 Re: List of Phobos functions that allocate memory? | ||||
---|---|---|---|---|
| ||||
Posted in reply to Marco Leise | On 2/8/2014 9:00 PM, Marco Leise wrote:
> The reasons for slow exceptions in D could be the generation
> of stack trace strings or the garbage collector instead of
> inherent trade offs to keep the successful code path fast.
Sigh, once again,
1. It is not the collector
2. I've implemented it both ways, I know what I'm talking about. You can see the fast exception way in the Win32 code generation, and the slow way in the Linux code generation.
|
February 09, 2014 Re: List of Phobos functions that allocate memory? | ||||
---|---|---|---|---|
| ||||
Posted in reply to Dmitry Olshansky | Am Sat, 08 Feb 2014 15:21:26 +0400 schrieb Dmitry Olshansky <dmitry.olsh@gmail.com>: > 08-Feb-2014 02:57, Jonathan M Davis пишет: > > On Friday, February 07, 2014 20:43:38 Dmitry Olshansky wrote: > >> 07-Feb-2014 20:29, Andrej Mitrovic пишет: > >>> On Friday, 7 February 2014 at 16:27:35 UTC, Andrei Alexandrescu wrote: > >>>> Add a bugzilla and let's define isValid that returns bool! > >>> > >>> Add std.utf.decode() to that as well. IOW, it should have an overload > >>> which returns a status code > >> > >> Much simpler - it returns a special dchar to designate bad encoding. And there is one defined by Unicode spec. > > > > Isn't that actually worse? > > No, it's better and more flexible for those who care to repair broken text in case it's broken. We currently have ZERO facilities to work with partly broken UTF and it's not that rare thing to have it. Your argument is unsubstantiated, since we have this already: http://dlang.org/phobos/std_encoding.html#.sanitize > > Unless you're suggesting that we stop throwing on > > decode errors, > > That is exactly what I suggest. > > then functions like std.array.front will have to check the > > result on every call to see whether it was valid or not and thus whether they should throw, which would mean extra overhead over simply having decode throw on decode errors. > > Why the heck? It will not throw either. In the very end bad encoding is handled by displaying the 'substituted' (typically '?') character in places where it broke not by throwing up hands in the air and spitting "UTF Exception: offset 4302 bad UTF sequence". This is not good enough (in case somebody though that it is). > > Those who care about throwing add a trivial map!(x => x != '\uFFFD' || die()) over a string, where die function throws an exception. Thats neither an improvement over calling "validate" nor does that deal with distinguishing between invalid UTF and \uFFFD in the input. > > validate has no business throwing, and we definitely should > > add isValidUnicode (or isValid or whatever you want to call it) for validation > > purposes. Code can then call that to validate that a string is valid and not > > worry about any UTFExceptions being thrown as long as it doesn't manipulate > > the string in a way that could result in its Unicode becoming invalid. > > Yet later down the road decode will triple check that anyway. Just saying. BTW if the string was checked beforehand there is no difference between 2 approaches at all (don't have to check). > > > However, I would argue that assuming that everyone is going to validate their strings and that pretty much all string-related functions shouldn't ever have to worry about invalid Unicode is just begging for subtle bugs all over the place IMHO. You're essentially dealing with error codes at that point, and I think that experience has shown quite clearly that error codes are generally a bad way to go. Almost no one checks them unless they have to. I think that having decode throw on invalid Unicode is exactly what it should be doing. The problem is that validate shouldn't. > > Every single text editor out there seems to disagree with you: they do show you partially substituted text, not a dialog box "My bad, it's broken UTF-8, I'm giving up!". Editor do different things. They often try to detect the encoding with a fall back to Latin1. If you open a file explicitly as UTF-8 they may display a substitution char or detect the error and use the fall back, as is the case with Geany and gedit does in fact throw an error message at you saying "My bad, it's broken UTF-8, I'm giving up!". -- Marco |
February 09, 2014 Re: List of Phobos functions that allocate memory? | ||||
---|---|---|---|---|
| ||||
Posted in reply to Walter Bright | On Saturday, February 08, 2014 21:21:40 Walter Bright wrote: > On 2/8/2014 2:11 PM, Dmitry Olshansky wrote: > > 09-Feb-2014 02:03, Walter Bright пишет: > >> On 2/7/2014 9:06 AM, Dmitry Olshansky wrote: > >>> Why throwing a single exception is such a big problem? > >> > >> Because in order to unwind the stack, you need to find the information about the stack layout. This lookup is rather slow. You can make the lookup faster by compromising the function code generation, but this is considered an unacceptable tradeoff. > > > > A special table lookup can't be slow compared to writing a dummy HTTP 500 response. Just saying. Yes, it's a tad slower then cmp + jz, I do understand that. > > > > Again I'm trying to say that framing stack unwinding as the culprit of vibe.d crawling under bad requests is plain wrong, and that was the focal point of the original argument. > > I don't know how vibe.d works, but my point is using exception handling to implement normal control flow is bad design and it is going to be slow and the reason it is slow is because of the table lookup and unwinding cost, and that is not going to be fixed. I wouldn't have considered throwing on an HTTP error to be "flow control." That's normal error handling, and throwing on HTTP errors is exactly what I would have done. It generally makes code a _lot_ cleaner that way, because you don't have to constantly check return codes for errors, and it's using exceptions for exactly what they're there for - reporting and handling errors. You don't want to use exceptions for stuff other than error reporting, and you don't want to use them in situations where the error case is the frequent case, but that shouldn't be the case for HTTP. Exceptions _will_ be slower than other code paths, and you don't want them to be the normal code path. Nothing is going to make exceptions as fast as the normal code paths either. However, D's exceptions are painfully slow - far slower than is reasonable - whether that's because of allocating the exception or unwinding the stack or creating the string for the stack trace or whatever is a matter for investigation, and I'm not about to claim that I know where the bottlenecks are. Fortunately, it looks like Adam Ruppe has found some ways to speed up exceptions: https://github.com/D-Programming-Language/druntime/pull/717 And there may be other improvements that we can implement as well. I agree that there's a limit to how much we can speed up exceptions, but right now, at minimum, we're getting creamed by Java in terms of speed: https://d.puremagic.com/issues/show_bug.cgi?id=9584 - Jonathan M Davis |
February 09, 2014 Re: List of Phobos functions that allocate memory? | ||||
---|---|---|---|---|
| ||||
Posted in reply to Walter Bright | On Saturday, February 08, 2014 14:13:04 Walter Bright wrote:
> On 2/7/2014 11:53 AM, Jonathan M Davis wrote:
> > or to avoid allocating them
>
> Grep for 'throw' in std.datetime shows that every throw is actually:
>
> throw new ...
>
> and an example:
>
> throw new DateTimeException("SYSTEMTIME cannot hold dates prior to the
> year 1601.");
>
> There is no requirement that the new is done there. You can preallocate the DateTimeException statically, and simply keep rethrowing the same exception instance.
>
> I.e. the allocation issue is a coding style issue, not a language problem.
Of course allocation is not a language issue. The question is whether (and how) we can change our approach to allocating exceptions in order to reduce their cost. And that's a change in how we approach them, not a change in the language itself. It might require some changes in druntime to better deal with other allocation schemes (particularly with how that affects exception chaining), but it's not a language issue.
And in general, I would expect that any speed-ups that we could attain with regards to actually throwing an exception would be in druntime's implementation rather than anything in the language itself. Any improvements there could then be combined with any improvements we could make to our approach to allocating exceptions (and for better or worse - probably worse - the normal approach at this point is to allocate a new exception when throwing).
- Jonathan M Davis
|
February 09, 2014 Re: List of Phobos functions that allocate memory? | ||||
---|---|---|---|---|
| ||||
Posted in reply to Walter Bright Attachments: | Am Sat, 08 Feb 2014 14:01:12 -0800 schrieb Walter Bright <newshound2@digitalmars.com>: > On 2/7/2014 8:40 AM, Dmitry Olshansky wrote: > > Meh. If exceptions are such a liability we'd better make them (much) faster. > > They can be made faster by slowing down non-exception code. > > This has been debated at length in the C++ community, and the generally accepted answer is that non-exception code performance is preferred and exception performance is thrown under the bus in order to achieve it. > > I think it's quite a reasonable conclusion. Am Sat, 08 Feb 2014 21:31:53 -0800 schrieb Walter Bright <newshound2@digitalmars.com>: > On 2/8/2014 9:00 PM, Marco Leise wrote: > > The reasons for slow exceptions in D could be the generation of stack trace strings or the garbage collector instead of inherent trade offs to keep the successful code path fast. > > Sigh, once again, > > 1. It is not the collector > > 2. I've implemented it both ways, I know what I'm talking about. You can see the fast exception way in the Win32 code generation, and the slow way in the Linux code generation. Ok, I'm on Linux which should be inherently slower at throwing exceptions as you say. So I've written a little test and it shows two things: 1. You are right, about the collector. It is not the bottleneck. 2. It doesn't have anything to do with trading speed for the successful code path either. I called two functions recursively until a nesting depth of 1000. The first version allocates a new exception, the second one reuses an existing exception. At the call site I caught the exception. I did this 10_000 times in a loop. [The code is attached.] Even at this nesting depth the second version still outperformed the first one by a factor of ~200(!) and all the CPU time (>98%) was is spent somewhere in libc. Using static exceptions (or similarly in C++: throwing literal strings) is VERY fast in D already and I see no reason to improve that at the moment. So I repeat my point: The reasons for slow exceptions in D could be the generation of stack trace strings or anything else other than some inherent trade offs to keep the successful code path fast. -- Marco |
February 09, 2014 Re: List of Phobos functions that allocate memory? | ||||
---|---|---|---|---|
| ||||
Posted in reply to Walter Bright | Am Sat, 08 Feb 2014 21:29:27 -0800 schrieb Walter Bright <newshound2@digitalmars.com>: > On 2/8/2014 2:59 PM, "Ola Fosheim Grøstad" <ola.fosheim.grostad+dlang@gmail.com>" wrote: > > On Saturday, 8 February 2014 at 22:03:13 UTC, Walter Bright wrote: > >> You can make the lookup faster by compromising the function code generation, but this is considered an unacceptable tradeoff. > > > > "Compromising"? You mean they had to modify codegen, which they didn't want to. Clearly, if you know the return address you also could have stack info access close to it (at a fixed offset), at no runtime cost whatsoever. > > Ola, I've done it both ways, I actually do know what I'm talking about. > > I've sometimes been proven wrong here, so you're welcome to do a pull request proving so. It is not the function code gen that needs to be improved on Linux, Walter. In fact that would be premature optimization considering that the *construction* of exceptions outweights unwinding costs for functions with no local variables by multiple orders of magnitude. -- Marco |
February 09, 2014 Re: List of Phobos functions that allocate memory? | ||||
---|---|---|---|---|
| ||||
Posted in reply to Jonathan M Davis | On Sunday, 9 February 2014 at 05:57:44 UTC, Jonathan M Davis wrote:
> Exceptions _will_ be slower than other code paths, and you don't want them to
> be the normal code path. Nothing is going to make exceptions as fast as the
> normal code paths either. However, D's exceptions are painfully
Just to be pedantic: this is not true.
If you have frame based exception meta-info recording then a throw out of recursion (without try-blocks in the recursion) will be faster than normal returns. You unwind down to the try-block with loading a register and a single JMP. All you have to do is to maintain a single linked list of stack frames that can catch. AFAIK the overhead is neglectible if you avoid doing try-blocks in light-weight function calls. You store one pointer per catching stack-frame.
That alone is good enough reason to realize that exception handling strategy should be a compiler switch, not a language policy. Because performance depends on what kind of code patterns you have and the architecture.
On current gen of x86 CPUs the decode stage of instructions into micro ops and pipelineing ought to be heavy enough that simple BRA instructions "disappear". Thus the offset strategy ought to work well too (injecting data into the code stream near the return point and branch over it if necessary, but usually not).
|
February 09, 2014 Re: List of Phobos functions that allocate memory? | ||||
---|---|---|---|---|
| ||||
Posted in reply to Ola Fosheim Grøstad | And with profiling you get the call-frequency between functions, so a throw could be replaced with: if (return_address = 0x1234556){...} // 60% if (return_address = 0x7899324){...} // 30% slow_unwinding() That ought to be obvious. |
Copyright © 1999-2021 by the D Language Foundation