views:

237

answers:

6

I don't really know much about the internals of compiler and JIT optimizations, but I usually try to use "common sense" to guess what could be optimized and what couldn't. So there I was writing a simple unit test method today:

@Test  // [Test] in C#
public void testDefaultConstructor() {
    new MyObject();
}

This method is actually all I need. It checks that the default constructor exists and runs without exceptions.

But then I started to think about the effect of compiler/JIT optimizations. Could the compiler/JIT optimize this method by removing eliminating the new MyObject(); statement completely? Of course, it would need to determine that the call graph does not have side effects to other objects, which is the typical case for a normal constructor that simply initializes the internal state of the object.

I presume that only the JIT would be allowed to perform such optimization. This probably means that it's not something I should worry about, because the test method is being performed only once. Are my assumptions correct?

Nevertheless, I'm trying to think about the general subject. When I thought about how to prevent this method from being optimized, I thought I may assertTrue(new MyObject().toString() != null), but this is very dependent on the actual implementation of the toString() method, and even then, the JIT can determine that toString() method always returns a non-null string (e.g. if actually Object.toString() is being called), and thus optimize the whole branch. So this way wouldn't work.

I know that in C# I can use [MethodImpl(MethodImplOptions.NoOptimization)], but this is not what I'm actually looking for. I'm hoping to find a (language-independent) way of making sure that some specific part(s) of my code will actually run as I expect, without the JIT interfering in this process.

Additionally, are there any typical optimization cases I should be aware of when creating my unit tests?

Thanks a lot!

+5  A: 

I think if you are worried about it getting optimized away, you may be doing a bit of testing overkill.

In a static language, I tend to think of the compiler as a test. If it passes compilation, that means that certain things are there (like methods). If you don't have another test that exercises your default constructor (which will prove it wont throw exceptions), you may want to think about why you are writing that default constructor in the first place (YAGNI and all that).

I know there are people that don't agree with me, but I feel like this sort of thing is just something that will bloat out your number of tests for no useful reason, even looking at it through TDD goggles.

Matt Briggs
Yes, testing overkill ftl.
MichaelGG
I agree with you. When I thought about it (after reading your answer), there must be other tests that use the default constructor if this test is more important that just being a "compiled" test. Thanks!
Hosam Aly
A: 

Why should it matter? If the compiler/JIT can statically determine no asserts are going to be hit (which could cause side effects), then you're fine.

MichaelGG
+4  A: 

Don't worry about it. It's not allowed to ever optimize anything that can make a difference to your system (except for speed). If you new an object, code gets called, memory gets allocated, it HAS to work.

If you had it protected by an if(false), where false is a final, it could be optimized out of the system completely, then it could detect that the method doesn't do anything and optimize IT out (in theory).

Edit: by the way, it cab also be smart enough to determine that this method:

newIfTrue(boolean b) {
    if(b)
        new ThisClass();
}

will always do nothing if b is false, and eventually figure out that at one point in your code B is always false and compile this routine out of that code completely.

This is where the JIT can do stuff that's virtually impossible in any non-managed language.

Bill K
Thank you. I wonder why the JIT behaves this way? If an object allocation is useless (as can actually be determined by static analysis in some cases), why wouldn't the JIT just optimize it?
Hosam Aly
I can now think of a corner case though, but I think it's rare enough. If the object allocation was done for example to ensure that enough memory is available for some other object(s) (and even making sure no paging will happen), the optimization would invalidate the assumption.
Hosam Aly
+2  A: 

Think about it this way:

Lets assume that compiler can determine that the call graph doesn't have any side effects(I don't think it is possible, I vaguely remember something about P=NP from my CS courses). It will optimize any method that doesn't have side effects. Since most tests don't have and shouldn't have any side effects then compiler can optimize them all away.

Alex Reitbort
Nice idea! I didn't think of that. :)
Hosam Aly
+1  A: 

It seems that in C# I could do this:

[Test]
public void testDefaultConstructor() {
    GC.KeepAlive(new MyObject());
}

AFAIU, the GC.KeepAlive method will not be inlined by the JIT, so the code will be guaranteed to work as expected. However, I don't know a similar construct in Java.

Hosam Aly
+1  A: 

Every I/O is a side effect, so you can just put

Object obj = new MyObject();
System.out.println(obj.toString());

and you're fine.

quant_dev
Yes, this certainly is a way of doing it. But unit tests usually shouldn't have output statements.
Hosam Aly
I think that as long as you're not relying on I/O to determine whether the test passed or failed, you're fine.
quant_dev