Here's a golden rule:
All the OLD optimization rules? Don't use them, they will actively
screw up your performance.
So (these all USED to be true for old versions of java! - beware of
any documents you find that state these things. Don't read them, they
are wrong):
WRONG: Don't create objects; creating them is expensive. Re-use them.
GOOD: Create objects. Loads of em. Especially if it means your objects
are immutable. Avoiding a 'synchronized' keyword gains you a
relatively huge amount of performance, and due to the garbage
collector's eden system, the system can handle 10 short lived objects
better than 1 long lived one. Either way, re-using objects is bad,
bad, bad, in all ways.
WRONG: Stick final on every method and every class, so that method
resolution is faster.
GOOD: That's just not relevant anymore; the JIT can figure this stuff
out quite well. Of course, there are purely code aesthetics reasons to
do it.
WRONG: Field access beats setters and getters.
GOOD: Not relevant anymore, and tends to lead to 'synchronized' usage,
or creating a lot of needless method (as mentioned, creating objects
isn't bad, but compared to simply handing out an existing one, it's
not good). Avoiding even 1 synchronized block buys you a couple
hundred virtual method lookups, and thousands of inlined getter calls.
Avoiding the copying mostly saves on memory, which is nice.
Something which is still true, but which will probably change sooner
rather than later:
DUBIOUS: Primitives beat objects; instead of bundling related state
into a class, pass a handful of primitives around.
REASON: This is definitely true. For example, a List<Byte> takes at
least 12x the space of a byte[], in bad cases (full 64-bit) even over
24x, and it's about 4 to 5 times slower. However, it's not entirely
infeasible for java to gain some sort of official @NonNull system so
the List itself can optimize this, and even without that, the JIT
compiler should be able to figure this out if the List never escapes,
or escapes only to a small set of methods.
DUBIOUS: Recursive methods are slower than rewriting it to be
interative.
REASON: True-ish, but, tail call optimization keeps being mentioned as
a likely candidate for the JVM at some point. Even without tail call
optimization, the difference is tiny. Of course, the stack is much
smaller than the heap, so if you find you'll be recursing for many
thousands of times, you SHOULD rewrite, at least to a tail-call-
optimizable form, and to be practical, to an iterative loop instead.
DUBIOUS: Lazily instantiate your singletons.
REASON: Most people who write that tired old getInstance() schtick to
create a singleton on the first call and return the old one on all
future calls get it wrong; the flag has to be volatile or your doubly-
nested if will actually fail on multi-core systems. Usually people are
thinking that every class that's in the entire classpath will be
initialized before your code run, which is obviously nuts. Upon first
use of a class, its initialized. So, just write: "private static final
INSTANCE = new MyClass();" in your singleton. The only point in the
lazy load scheme is if your singleton also has static classes that
code might use, but why would you do that to a singleton? Class
Loading is guaranteed to be streamlined properly on a multi-core
system (1 class will never be loaded and/or initialized twice due to a
threading issue).
Things that really do help performance:
RIGHT: Use java.util.concurrent instead of trying to handroll your
threading with synchronized blocks all over the place. An
AtomicInteger is way quicker than synchronizing access to 1 handrolled
mutable Integer instance. Also, 'volatile' beats synchronized, if you
know how to use it.
RIGHT: Always, always, think about the performance of your algorithm.
Computer science students know this stuff as big-O and little-O
notation. For example, quicksorting twice as much data takes 2*log(2)
more time - virtually linear to the search data size. Bubblesort takes
4 times as long to sort twice as much data (n^2). That's going to run
out of control on big data sets.
RIGHT: Profile, profile, profile. If there's a performance issue, fire
up a profiler, and figure out where. Even if it really ends up being a
nickel-and-dime situation, where loads of tiny performance issues
killed you, the vast majority of your code is run only once, or very
rarely, while your app spends 95% of the time in 5% of your code (the
80-20 rule, on steroids). Let's say you really need to nickle-and-dime
your way out of your performance issue, then only by profiling do you
know where you should be spending the time rewriting your code (and
making it ugly in the process). More likely there's a specific thing
you're doing which is eating massive amounts of CPU, and profiling
will find it for you.
RIGHT: Cache expensive operations. Use Google Collections API to
easily create caches that don't get in the way of garbage collecting.
Or just use a simple LRU cache with LinkedHashMap.
RIGHT: Some things are just too complicated for one computer. Design
things so that its easy to shard your calculations if you expect that
the program will at some point in time need to handle data that's just
too much for one computer to bear, even if you handrolled perfectly
designed assembler. It's far cheaper to buy 10 budget boxes and wire
them up, than buy one mega server, and either option is waay cheaper
than spending 2 manyears rewriting code.
On Oct 12, 4:01 pm, Moandji Ezana <
mwa...@gmail.com> wrote: