0

I understand the JVM optimizes some things for you (not clear on which things yet), but lets say I were to do this:

while(true) {
     int var = 0;
}

would doing:

int var;
while(true) {
     var = 0;
}

take less space? Since you aren't declaring a new reference every time, you don't have to specify the type every time.

I understand you really would only need to put var outside of while if I wanted to use it outside of that loop (instead of only being able to use it locally like in the first example). Also, what about objects, would it be different that primitive types in that situation? I understand it's a small situation, but build-up of this kind of stuff can cause my application to take a lot of memory/cpu. I'm trying to use the least amount of operations possible, but I don't completely understand whats going on behind the scenes.

If someone could help me out, even maybe link me to somewhere I can learn about saving cpu by decreasing amount of operations, it would be highly appreciated. Please no books (unless they're free! :D), no way of getting one right now /:

Vince
  • 14,470
  • 7
  • 39
  • 84
  • 4
    This is *the* definition of an unnecessary micro-optimization. I bet you they compile to the same thing, but you can check if you really think it matters. – Ry- Mar 09 '14 at 21:00
  • 7
    Declaring variables with larger scope than required is generally a *bad* practice for performance. You make it harder for the JIT to figure out what is safe in-line and/or eliminate. In the first example it can trivially tell that your value never leaves the loop and can use just a register to hold it. In the second example it might not be able to determine if you ever actually use the last value of var later in the program and will have to allocate stack or heap space for it. – Affe Mar 09 '14 at 21:04
  • 1
    int´s (all primitives) in Java aren´t references. Any sane compiler will optimize this, like @minitech said. And if you´re that worried about performance and memory, forget Java, use pure assembler instead. (of coure this makes only sense if you´re good enough to be better than compiler optimization) – deviantfan Mar 09 '14 at 21:05
  • While, as Affe suggests, using a larger than necessary scope for a variable is bad practice, relatively speaking a reference or scalar variable takes a trivial amount of space. And it so happens that, for the above example, the generated bytecodes would probably be identical. However, you seem to mistakenly believe that it takes some sort of effort to "create" a local variable. This is not the case -- creating a variable is "free" and costs nothing. – Hot Licks Mar 09 '14 at 21:16
  • 1
    Dirty little secret: Java bytecodes provide no way to "allocate" a local variable within an inner scope in a method. All locals must be allocated on method entry. The best that can happen is for the compiler to recognize that the scope of two variables does not overlap and hence allocate them to the same local variable slot. (But this allocation occurs as a part of the method entry protocol and is equally expensive in CPU regardless of whether space is allocated for no locals or 1000.) – Hot Licks Mar 09 '14 at 21:18
  • @deviantfan - Local variables, whether `int` or reference, take the same (trivial) amount of space in the stack, cost zero to allocate, and zero to free. The only real "cost" associated with the variable itself is if GC occurs while the method is invoked -- then reference variables must be "marked". – Hot Licks Mar 09 '14 at 21:23
  • 1
    If you want to improve performance and reduce storage usage the main rule is: Don't create unnecessary objects. A classical case is "parsing" a String using successive `substring` calls to separate the parsed word and the "remainder". (The second rule is: Beware of large aggregates or object networks.) – Hot Licks Mar 09 '14 at 21:29

2 Answers2

2

Don't. Premature optimization is the root of all evil.

Instead, write your code as it makes most sense conceptually. Write it thoughtfully, yes. But don't think you can be a 'human compiler' and optimize and still write good code.

Once you have written your code (more or less naively, depending on your level of experience) you write performance tests for it. Try to think of different ways in which the code may be used (many times in a row, from front to back or reversed, many concurrent invocations etc) and try to cover these in test cases. Then benchmark your code.

If you find that some test cases are not performing well, investigate why. Measure parts of the test case to see where the time is going. Zoom into the parts where most time is spent.

Mostly, you will find weird loops where, upon reading the code again, you will think 'that was silly to write it that way. Of course this is slow' and easily fix it. In my experience most performance problems can be solved this way and 'hardcore optimization' is hardly ever needed.

In the end you will find that 99* percent of all performance problems can be solved by touching only 1 percent of the code. The other code never comes into play. This is why you should not 'prematurely' optimize. You will be spending valuable time optimizing code that had no performance issues in the first place. And making it less readable in the process.

Numbers made up of course but you know what I mean :)

Hot Licks points out the fact that this isn't much of an answer, so let me expand on this with some good ol' perfomance tips:

  1. Keep an eye out for I/O

    Most performance problems are not in pure Java. Instead they are in interfacing with other systems. In particular disk access is notoriously slow. So is the network. So minimize it's use.

  2. Optimize SQL queries

    SQL queries will add seconds, even minutes, to your program's execution time if you don't watch out. So think about those very carefully. Again, benchmark them. You can write very optimized Java code, but if it first spends ten seconds waiting for the database to run some monster SQL query than it will never be fast.

  3. Use the right kind of collections

    Most performance problems are related to doing things lots of times. Usually when working with big sets of data. Putting your data in a Map instead of in a List can make a huge difference. Also there are specialized collection types for all sorts of performance requirements. Study them and pick wisely.

  4. Don't write code

    When performance really matters, squeezing the last 'drops' out of some piece of code becomes a science all in itself. Unless you are writing some very exotic code, chances are great there will be some library or toolkit to solve your kind of problems. It will be used by many in the real world. Tried and tested. Don't try to beat that code. Use it.

We humble Java developers are end-users of code. We take the building blocks that the language and it's ecosystem provides and tie it together to form an application. For the most part, performance problems are caused by us not using the provided tools correctly, or not using any tools at all for that matter. But we really need specifics to be able to discuss those. Benchmarking gives you that specifity. And when the slow code is identified it is usually just a matter of changing a collection from list to map, or sorting it beforehand, or dropping a join from some query etc.

Stijn de Witt
  • 40,192
  • 13
  • 79
  • 80
  • I believe that's a comment, not an answer. – Hot Licks Mar 09 '14 at 21:16
  • 1
    It is an answer. The question was: 'Can you/How do you ...'. The answer is 'Don't!'... Ok maybe it's advice... :) – Stijn de Witt Mar 09 '14 at 21:21
  • Advice closer to answering the OP's question would be along the lines of "don't create unnecessary objects", and no one has (until now) mentioned objects themselves. – Hot Licks Mar 09 '14 at 21:25
  • No I don't agree. OP thinks he needs to learn this kind of stuff so he will be able to write faster/better code. I am telling him don't try. Not creating unnecessary *anything* is common sense. No need to study compilers to figure that out. And that is the point of my answer. Don't try to beat the compiler. Don't write code that is 'theoretically fast'. Instead, write code that makes sense to us humans. Then test it and see where (if any) the performance problems are. Then fix those. They will not be in the kind of constructs OP is thinking about right now. They will be much more mundane. – Stijn de Witt Mar 09 '14 at 21:38
  • Ah, but on one hand you're telling him "don't micro-optimize by trying to reduce local allocations", but on the other hand "obviously, anything unnecessary should not be done". The question is, which things *should* one worry about?? – Hot Licks Mar 09 '14 at 21:41
  • If you insist on that strict division I'd say no things. Just get the program running already! Then **test** it. Benchmarks! That is where insight comes from. And those benchmarks will show you all kinds of stupid slow code, but it won't be such things. – Stijn de Witt Mar 09 '14 at 21:44
  • You've obviously never spent weeks convincing a big client that their programmers didn't understand good Java programming practices when they wrote a large commercial application. An aphorism older than "no premature optimization" is "you can't test quality into a product". – Hot Licks Mar 09 '14 at 21:50
  • (Your "serial downvoting" is really childish, by the way.) – Hot Licks Mar 09 '14 at 21:54
  • Whatever. In 20 years of programming I never encountered (let alone wrote myself) code that the kind of things OP is looking at would matter for. Such code does exist of course but we mere mortals never touch it. Java is not the language for such code. But to each it's own if you think such optimization is useful by all means go ahead. – Stijn de Witt Mar 09 '14 at 21:54
  • And I did not downvote anything! Why do you think this is personal? I did not vote (up or down) on any item in this question. – Stijn de Witt Mar 09 '14 at 21:55
  • The OP should be respected for wondering about this. The fact that he is wondering about the wrong issue is obviously due to inexperience and unfamiliarity with how Java works, but his heart's in the right place. He's wondering "What are good Java programming practices?" In my 40 years of programming experience I find that a good question to be constantly asking. – Hot Licks Mar 09 '14 at 21:56
  • Exactly I fully agree. And I am hoping to help him by guiding him out of the woods he seems to be in. He will be (imho) much better of spending his time writing some test code and **benchmarking** it to find out where his CPU time is spent in his actual code. Than figuring out how to fix those actual performance bottlenecks. – Stijn de Witt Mar 09 '14 at 21:59
  • So he shouldn't attempt to learn from others (except you). – Hot Licks Mar 09 '14 at 22:01
  • Why do you keep trying to make this something personal? Why don't you just write a better answer? SO keeps telling me to take this into chat... do you want to? – Stijn de Witt Mar 09 '14 at 22:03
2

Attempting to optimise code which doesn't need to be optimised increases complexity and decreases readability.

However, there are cases were improving readability also comes with improved performance.

For example,

  • if a numeric value cannot be null, use a primitive instead of a wrapper. This makes it clearer that the value cannot be null but also uses less memory and reduces pressure on the GC.
  • use a Set when you have a collection which cannot have duplicates. Often a List is used when in fact a Set would be more appropriate, depending on the operations you perform, this can also be faster by reducing time complexity.
  • consider using an enum with one instance for a singleton (if you have to use singletons at all) This is much simpler as well as faster than double check locking. Hint: try to only have stateless singletons.
  • writing simpler, well structured code is also easier for the JIT to optimise. This is where trying to out smart the JIT with more complex solutions will back fire because you end up confusing the JIT and what you think should be faster is actually slower. (And it's more complicated as well)
  • try to reduce how much you write to the console (and IO in general) in critical sections. Writing to the console is so expensive, both for the program and the poor human having to read it that is it worth spending more time producing concise console output.
  • try to use a StringBuilder when you have a loop of elements to add. Note: Avoid using StringBuilder for one liners, just series of append() as this can actually be slower and harder to read.

Perfection is achieved, not when there is nothing more to add, but when there is nothing left to take away. -- Antoine de Saint-Exupery, French writer (1900 - 1944)

Developers like to solve hard problems and there is a very strong temptation to solve problems which don't need to be solved. This is a very common behaviour for developers of up to 10 years experience (it was for me anyway ;), after about this point you have already solved most common problem before and you start selecting the best/minimum set of solutions which will solve a problem. This is the point you want to get to in your career and you will be able to develop quality software in far less time than you could before.

If you dream up an interesting problem to solve, go ahead and solve it in your own time, see what difference it makes, but don't include it in your working code unless you know (because you measured) that it really makes a difference.

However, if you find a simpler, elegant solution to a problem, this is worth including not because it might be faster (thought it might be), but because it should make the code easier to understand and maintain and this is usually far more valuable use of your time. Successfully used software usually costs three times as much to maintain as it cost to develop. Do what will make the life of the poor person who has to understand why you did something easier (which is harder if you didn't do it for any good reason in the first place) as this might be you one day ;)

A good example on when you might make an application slower to improve reasoning, is in the use of immutable values and concurrency. Immutable values are usually slower than mutable ones, sometimes much slower, however when used with concurrency, mutable state is very hard to get provably right, and you need this because testing it is good but not reliable. Using concurrency you have much more CPU to burn so a bit more cost in using immutable objects is a very sensible trade off. In some cases using immutable objects can allow you to avoid using locks and actually improve throughput. e.g. CopyOnWriteArrayList, if you have a high read to write ration.

Peter Lawrey
  • 525,659
  • 79
  • 751
  • 1,130