12

I currently have a disagreement going on with my 2nd year JAVA professor that I'm hoping y'all could help solve:

The code we started with was this:

   public T peek()
   {
       if (isEmpty())
       .........
   }
   public boolean isEmpty() 
   {
       return topIndex<0;
   }

And she wants us to remove the isEmpty() reference and place its code directly into the if statement (i.e. change the peek method contents to: if(topIndex<0).......) to "Make the code more efficient". I have argued that a) the runtime/compile time optimizer would most likely have inlined the isEmpty() call, b) even if it didn't, the 5-10 machine operations would be negligible in nearly every situation, and c) its just bad style because it makes the program less readable and less changeable.

So, I guess my question is: Is there any runtime efficiency gained by inlineing logic as opposed to just calling a method? I have tried simple profiling techniques (aka long loop and a stopwatch) but tests have been inconclusive.

EDIT:

Thank you everyone for the responses! I appreciate you all taking the time. Also, I appreciate those of you who commented on the pragmatism of arguing with my professor and especially doing so without data. @Mike Dunlavey I appreciate your insight as a former professor and your advice on the appropriate coding sequence. @ya_pulser I especially appreciate the profiling advice and links you took the time to share.

isquaredr
  • 187
  • 8
  • 1
    I think you're right, but there is no point arguing with your professor. Sometimes in life you have to right the code as you're told. – khelwood Oct 03 '15 at 10:45
  • 1
    You're almost certainly right; but maybe your professor intends to let the students discover the pointlessness of this kind of micro-optimisation for themselves. – Dawood ibn Kareem Oct 03 '15 at 10:48
  • 2
    *Sigh* Premature optimization is the root of all evil. But getting into conflict with your professor is not a wise choice, either. – RealSkeptic Oct 03 '15 at 10:49
  • Here is a similar question that I answered for C#. The values cannot be drastically different between C# and Java for a method call... http://stackoverflow.com/questions/32663051/performance-cost-of-method-encapsulation/32663206#32663206 – displayName Oct 03 '15 at 18:10
  • @displayName: Actually, they *can* differ, because the most widely used implementation of C# (the combination of Microsoft Visual C# and Microsoft CLR) does not perform dynamic adaptive optimizations to the same degree that the most widely used implementation of Java (the combination of Oracle `javac` and Oracle HotSpot) does, so there *may* be cases, there the CLR JITter cannot statically prove that it is legal to inline a polymorphic method, whereas the adaptive optimizer in HotSpot can just look *at runtime* whether a method is overridden or not. However, that's likely not the case here. – Jörg W Mittag Oct 03 '15 at 18:57
  • @JörgWMittag: In that case, Java is only likely to be better at inlining. Also, the testing machine is of 2003 in the article I have linked to and it's end 2015 currently. Only gives us reasons to not perform the in-lining ourselves and focus on the readability. I would surely like to read an article similar to the one linked in my answer about C# but I have been away from Java for ~2 years. Do you know of any? – displayName Oct 03 '15 at 19:04
  • Been reading Knuth, eh? – jpmc26 Oct 04 '15 at 02:14
  • Possible duplicate of [java how expensive is a method call](http://stackoverflow.com/questions/6495030/java-how-expensive-is-a-method-call) – Raedwald Oct 05 '15 at 07:17
  • I just wanted to echo some of the comments above about not getting into conflict with your professor. When I was learning to code, I was taught that instead of using division, you should always multiply because it was "faster" computationally. But in the real world, with modern CPUs, the effect of using division is negligible in most cases. But the point of the lesson was to be aware of how my code effects processing, memory management, etc. So my advice is, there's a lot more you are learning in this class than just coding. I think you're picking up on that. :) – NoChinDeluxe Oct 22 '15 at 14:24
  • @JörgWMittag Hotspot doesn't particularly care if overrides of a method exist. Even if overrides exist hotspot will inline `isEmpty` if you are always calling `peek` on the same exact type, and it will insert a check to verify that you used the expected type (a so-called uncommon trap). If there are two types used the JVM will happily inline both, and if there are more it will compile to an indirect jump. I'm sure Hotspot can also do stuff based on if overrides exist or not, but these optimizations don't depend on that but only on what you actually use at that particular spot. – JanKanis Mar 13 '17 at 16:17

5 Answers5

10

You are correct in your assumptions about java code behaviour, but you are impolite to your professor arguing without data :). Arguing without data is pointless, prove your assumptions with measurements and graphs.

You can use JMH ( http://openjdk.java.net/projects/code-tools/jmh/ ) to create a small benchmark and measure difference between:

  • inlined by hand (remove isEmpty method and place the code in call place)
  • inlined by java jit compiler (hotspot after 100k (?) invocations - see jit print compilation output)
  • disabled hotspot inlining at all

Please read http://www.oracle.com/technetwork/java/whitepaper-135217.html#method

Useful parameters could be:

  • -Djava.compiler=NONE
  • -XX:+PrintCompilation

Plus each jdk version has it's own set of parameters to control jit.

If you will create some set of graphics as results of your research and will politely present them to the professor - I think it will benefit you in future.

I think that https://stackoverflow.com/users/2613885/aleksey-shipilev can help with jmh related questions.

BTW: I had great success when I inlined plenty of methods into a single huge code loop to achieve maximum speed for neural network backpropagation routine cause java is (was?) too lazy to inline methods with methods with methods. It was unmaintainable and fast :(.

Community
  • 1
  • 1
ya_pulser
  • 2,620
  • 2
  • 16
  • 21
  • 1
    Experience is data, too. – usr Oct 03 '15 at 17:13
  • Aren't all methods shorter than 35 bytes inlined, whether they are hot or not? – Rosa Oct 04 '15 at 14:11
  • 1
    @cantido inlining takes time and slows down "start up" time of JVM aka "main issues in java that it is slow when you start your app". jit kicks in later when it has statistics which methods are hot. You can observe as methods being inlined by starting with -XX:+PrintCompilation. Btw: 35 bytes is really small amount and you can tune it by jvm start parameters. – ya_pulser Oct 04 '15 at 18:04
8

Sad...

I agree with your intuitions about it, especially "the 5-10 machine operations would be negligible in nearly every situation".

I was a C.S. professor a long time ago. On one hand, professors need all the slack you can give them. Teaching is very demanding. You can't have a bad day. If you show up for a class and you're not fully prepared, you're in for a rough ride. If you give a test on Friday and don't have the grades on Monday the students will say "But you had all weekend!" You can get satisfaction from seeing your students learn, but you yourself don't learn much, except how to teach.

On the other hand, few professors have much practical experience with real software. So their opinions tend to be founded on various dogmatic certitudes rather than solid pragmatism.

Performance is a perfect example of this. They tend to say "Don't do X. Do Y because it performs better." which completely misses the point about performance problems - you have to deal in fractions, not absolutes. Everything depends on what else is going on. The way to approach performance is, as someone said "First make it right. Then make it fast."

And the way you make it fast is not by eyeballing the code (and wondering "should I do this, or should I do that"), but by running it and letting it tell you how it's spending time. The basic idea of profiling is how you do this. Now there is such a thing as bad profiling and good profiling, as explained in the second answer here (and usually when professors do teach profiling, they teach the bad kind), but that's the way to go.

Mike Dunlavey
  • 40,059
  • 14
  • 91
  • 135
  • Though "make it right" means not too horribly inefficient (completely inappropriate algorithms, complete disregard for performance), nor gratuitiously so (using a probably less efficient but not more concise/readable way). Sometimes changing algorithms (that's where you get the most for your effort) too late is as bad or worse than a rewrite. I'm most specifically *not* saying that's the case for the code OP gave. – Deduplicator Oct 03 '15 at 19:27
  • 1
    @Deduplicator: Sure, but people don't knowingly write very bad big-O stuff. I've done it unintentionally, but a few samples pinpointed the issue and it was easily fixed. It bothers me how many people seem to think all you have to do is get your big-O right and you're home free. [*Nothing could be further from the truth.*](http://stackoverflow.com/a/927773/23771) – Mike Dunlavey Oct 03 '15 at 23:23
5

As you say, the difference will be small, and in most circumstances the readability should be a higher priority. In this case though, since the extra method consists of a single line, I'm not sure this adds any real readability benefit unless you're calling the same method from elsewhere.

That said, remember that your lecturer's target is to help you learn computer science, and this is a different priority than writing production code. Particularly, she won't want you to be leaving optimization to automated tools, since that doesn't help your learning.

Also, just a practical note - in school and in professional development, we all have to adhere to coding standards we personally disagree with. It's an important skill, and really is necessary for team working, even if it does chafe.

hugh
  • 2,237
  • 1
  • 12
  • 25
  • "_in school… we all have to adhere to coding standards we disagree with_" Like that one teacher who insists that you use do-whiles instead of whiles in every possible circumstance. +1 for that note – that thinking may be depressing, but it's gonna save your butt quite a few times if you prioritize making your bosses happy over making good code. – Nic Oct 03 '15 at 11:24
  • 1
    "and this is a different priority than writing production code" This summarizes the depressing state of CS education. Creating productive engineers is not part of the goal. – usr Oct 03 '15 at 17:14
  • 1
    Q: I'd take a more optimistic view, that consistency is vital on large projects, and often outweighs the (frankly, often negligible) gains between different conventions. Much better to understand and adapt than die in a ditch insisting *your* way is the only right way. – hugh Oct 03 '15 at 17:28
3

Calling isEmpty is idiomatic and nicely readable. Manually inlining that would be a micro optimization, something best done in performance critical situations, and after a bottleneck was confirmed by benchmarking in the intended production environment.

Is there a real performance benefit in manually inlining? Theoretically yes, and maybe that's what the lecture wanted to emphasize. In practice, I don't think you'll find an absolute answer. The automatic inlining behavior maybe implementation dependent. Also keep in mind that benchmark results will depend on JVM implementation, version, platform. And for that reason, this kind of optimization can be useful in rare extreme situations, and in general detrimental to portability and maintainability.

By the same logic, should we inline all methods, eliminating all indirections at the expense of duplicating large blocks of code? Definitely not. Where you draw the line exactly between decomposition and inlining may also depend on personal taste, to some degree.

janos
  • 120,954
  • 29
  • 226
  • 236
0

Another form of data to look at could be the code that is generated. See the -XX:+PrintAssembly option and friends. See How to see JIT-compiled code in JVM? for more information.

I'm confident that in this particular case the Hotspot JVM will inline the call to isEmpty and there would be no performance difference.

Community
  • 1
  • 1
JanKanis
  • 6,346
  • 5
  • 38
  • 42