www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - assertNotThrown (and asserts in general)

reply Malte <no valid.mail> writes:
I was interested by asserts and how the compiler uses them to 
optimize the code. So I looked at the compiler explorer to see 
how and found it, it doesn't.

What I tried to do is turn a std.conv.to!ulong(byte) to a simple 
cast with the help of assertions.
https://godbolt.org/g/4uckWU

If there is an assert right before an if with the same condition, 
I think it should remove the compare and jump in release, but it 
doesn't. I'm assuming the compilers just aren't ready yet, but 
should be someday. At least that is what the documentation 
promises.

More curious made me 
"assertNotThrown!ConvOverflowException(input.to!ubyte)" where it 
didn't even remove the assert code in release and produced the 
most inefficient assembly.
Is that intended behaviour? In my opinion that greatly limits the 
usabilty of it.
May 21 2018
parent reply Jonathan M Davis <newsgroup.d jmdavisprog.com> writes:
On Monday, May 21, 2018 12:44:21 Malte via Digitalmars-d-learn wrote:
 I was interested by asserts and how the compiler uses them to
 optimize the code. So I looked at the compiler explorer to see
 how and found it, it doesn't.

 What I tried to do is turn a std.conv.to!ulong(byte) to a simple
 cast with the help of assertions.
 https://godbolt.org/g/4uckWU

 If there is an assert right before an if with the same condition,
 I think it should remove the compare and jump in release, but it
 doesn't. I'm assuming the compilers just aren't ready yet, but
 should be someday. At least that is what the documentation
 promises.
Walter wants to use assertions to then have the compiler make assumptions about the code and optimized based on it, but he hasn't implemented anything like that, and there are a number of arguments about why it's a very bad idea - in particular, if it allows the compiler to have undefined behavior if the assertion would have failed if it were left in. So, what is actually going to happen with that is unclear. There are folks who want additional performance benefits by allowing assertions to work as hints to the compiler, and there are folks who want them to truly just be for debugging purposes, because they don't want the compiler to then generate code that makes the function behave even more badly when the assertion would have failed but had been compiled out. Personally, my big concern is that it can't introduce undefined behavior, or it would potentially violate memory safety in safe code, which would then mean that using assertions in safe code could make your code effectively system, which would defeat the whole purpose of safe. If the compiler can optimize while not breaking those guarantees, then I'm all for it, but some folks disagree with even that, because they don't want their code to behave worse when something goes wrong. Of course, the counter-argument is that if they're that worried about it, they should leave the checks in, but there definitely isn't agreement on the matter. Walter is in favor of adding optimizations based on assertions though, so I think that there's a decent chance that we'll see something like that at some point. But language feature stuff (like implementing scope for DIP 1000) are generally much higher priority than adding extra optimizations to the compiler. So, while optimizations and performance improvements are certainly done, they're not the main focus right now.
 More curious made me
 "assertNotThrown!ConvOverflowException(input.to!ubyte)" where it
 didn't even remove the assert code in release and produced the
 most inefficient assembly.
 Is that intended behaviour? In my opinion that greatly limits the
 usabilty of it.
assertNotThrown doesn't use any assertions. It explicitly throws an AssertError (which is what a failed assertion does when it's not compiled out). assertNotThrown would have to use a version(assert) block to version the checks to try and mirror what the assert statement does. However, assertNotThrown is specifically intended for unit tests. IIRC, assertions in unit tests are left in when compiled with -unittest (otherwise, compiling with -release and -unittest - like Phobos does for one of its passes as part of its unittest build - would not work), but I don't think that the assertions outside of unittest blocks get left in in that case, so using version(assert) on assertThrown or assertNotThrown might break them. I'm not sure. Regardless, using them for testing what assertions do is just wrong. You need to test actual assert statements if that's what you want to be testing. - Jonathan M Davis
May 21 2018
parent Malte <no valid.mail> writes:
On Monday, 21 May 2018 at 19:44:17 UTC, Jonathan M Davis wrote:
 Walter wants to use assertions to then have the compiler make 
 assumptions about the code and optimized based on it, but he 
 hasn't implemented anything like that, and there are a number 
 of arguments about why it's a very bad idea - in particular, if 
 it allows the compiler to have undefined behavior if the 
 assertion would have failed if it were left in. So, what is 
 actually going to happen with that is unclear. There are folks 
 who want additional performance benefits by allowing assertions 
 to work as hints to the compiler, and there are folks who want 
 them to truly just be for debugging purposes, because they 
 don't want the compiler to then generate code that makes the 
 function behave even more badly when the assertion would have 
 failed but had been compiled out.
If your code is based on untrue assumptions, you probably have a bug anyways. If you used asserts and an optimization brought it in, you will at least find it as soon as you remove the release flag. It shouldn't be a problem to make it a compiler flag for those who don't want it. Defaulted to true with -O3 but can be turned off with -fno-assert-optimize or something like that.
 Personally, my big concern is that it can't introduce undefined 
 behavior, or it would potentially violate memory safety in 
  safe code, which would then mean that using assertions in 
  safe code could make your code effectively  system, which 
 would defeat the whole purpose of  safe.
Fair point, that probably limits the optimiations that can be done. If I have an assert that an array has 10 elements when it actually has only 3 and do some operations on it, that could read/write to memory I have never allocated. However some optimations should still be possible in SafeD, like ignoring if conditions where the results are known at compile time if the asserts are true. Or loop unrolling and auto-vectorization without checking for the rest should also be possible if you have an assert, that the length of an array is divisible by something. Neither of them should be able to add unsafe instructions. The worst that could happen is relying on a wrong value to access an element of an array and fail a bounds check.
 assertNotThrown doesn't use any assertions. It explicitly 
 throws an AssertError (which is what a failed assertion does 
 when it's not compiled out). assertNotThrown would have to use 
 a version(assert) block to version the checks to try and mirror 
 what the assert statement does. However, assertNotThrown is 
 specifically intended for unit tests. IIRC, assertions in unit 
 tests are left in when compiled with -unittest (otherwise, 
 compiling with -release and -unittest - like Phobos does for 
 one of its passes as part of its unittest build - would not 
 work), but I don't think that the assertions outside of 
 unittest blocks get left in in that case, so using 
 version(assert) on assertThrown or assertNotThrown might break 
 them. I'm not sure. Regardless, using them for testing what 
 assertions do is just wrong. You need to test actual assert 
 statements if that's what you want to be testing.
Okay, clearly a misunderstanding on my side then. Thanks for clarifying that.
May 23 2018