On Wed, Nov 29, 2017 at 07:29:56PM -0800, Walter Bright via Digitalmars-d wrote: > On 11/29/2017 7:15 PM, Jonathan M Davis wrote: > > I wouldn't have expected assertions to cost much more than however > > much it costs to evaluate the expression being asserted unless the > > assertion fails. Now, even that can slow down a program a fair bit, > > depending on what's being asserted and how many assertions there > > are, but it's not something that I would have expected to vary > > particular between C and D. It doesn't surprise me that the > > generated code would be larger than you'd get for the same > > assertions in C because how assertions are handled when they fail is > > quite different, but I would expect the assertions themselves to > > cost about the same in terms of performance as long as they don't > > fail. What's going on that's making them so much worse? > > The code *size* causes problems because it pushes the executing code > out of the cache. Another issue (I should check this again) was doing > null checks on member function calls, which is not necessary since if > they're null it'll seg fault.
Can you elaborate? Because in my current understanding, assert(expr) is implemented by evaluating expr, which is unavoidable, and if it fails, calls a function in druntime to handle the failure. So as far as the user's code is concerned, there shouldn't be any performance issue -- presumably, druntime's assert() implementation shouldn't even be in the cache because it's not being used (up to that point). It's just a single function call in the user's code, which is at most just a few bytes. What happens inside the assert() implementation seems to be irrelevant because at that point your program is going to terminate anyway. So a cache miss for the assert() implementation isn't going to be a big deal (unless your program is asserting at a high frequency, in which case you have bigger problems than performance!). Unless you're talking about applications where the entire program must fit in cache or flash or SRAM or whatever. In that case, perhaps the solution is to have a different druntime that has a leaner implementation of assert(). Which brings us to the implementation of assert() itself. What about it makes it so big? I suspect most of the bloat comes from throwing AssertError, which pulls in the stack-unwinding code, which, if my memory is still up to date, suffers from performance issues where it tries to construct the stacktrace regardless of whether or not the catch block actually wants the stacktrace. I vaguely remember suggesting that this should be done lazily, so that the actual construction of the stacktrace (including symbol lookups, etc.) isn't done until somebody actually asks for it. You'd still have to save the addresses of the call somewhere, since otherwise it might get overwritten by the time the stack unwinding is done, but it should be a lot cheaper than doing symbol lookups eagerly. But perhaps this issue has since been fixed? But of course, this assumes that we even need to throw AssertError in the first place. If this can be made optional, we can skip the stack unwinding code altogether. (But I can see that this will only work for specific applications, since you may not be able to avoid the need for the unwinding code to call dtors and stuff to free up allocated resources, etc., which, if it's necessary, means you can't avoid linking in the stack unwinding code. But it *can*, at least in theory, be something separate from the stacktrace construction code, so you can still save a bit of code there. Make the stacktrace construction code a zero-argument template, then it won't get linked into the executable unless it's actually used.) T -- Let's not fight disease by killing the patient. -- Sean 'Shaleh' Perry
