Skip to content

The 10 principles of Assembly Java

Assembly Java is a particularly economical and efficient style of usage of the Java language. These are the principles of Assembly Java:

#0: Define as few classes as possible. Ideally, use a single class.

How: Pack as much as possible in each class. Merge conceptually-unrelated classes together, when possible.
Why: Every class incurs a space-overhead (of about 200 bytes) in the final jar. Having everything in one class also improves the class-locality (as all the methods/fields you use are likely to be from the same class).
Limit: The minimum number of classes you may use is given by the number of classes that you have to extend, as you have to define a new class for each inheritance.

#1: Define as few methods as possible.

How: pack as much of sequential logic as you can in a single big method. Don’t split methods solely for clarity’s sake; merge them.
Why: Performance gain by avoiding method call overhead. Space gain by avoiding the overhead of the method declaration.
Limit: You need a separate method for each method you overload, in particular for each abstract method you implement.

#2: Avoid polymorphism; use switch() instead.

How: Rather than declaring a base interface or abstract class, and extend it in order to polymorphically customize behavior,
use a single concrete class with switch statements for all variable behavior. Declare integer constants identifying the behavior cases.
Why: Size gain by reducing the number of classes (#0 above) (abstract classes and interfaces are as bad as any other class). Performance gain by avoiding the virtual dispatch.

Bad Assembly Java:


abstract class Animal {
  abstract String saySomething();
}

class Dog extends Animal {
  String saySomething() { return "how how"; }
}

class Cat extends Animal {
  String saySomething() { return "meow"; }
}

Good Assembly Java:


class Animal {
  static final int DOG=1, CAT=2;
  String saySomething(int kind) {
    switch (kind) {
      case DOG: return "how how";
      case CAT: return "meow";
      default: return null;
   }
}

#3: Avoid object instantiation (object creation).

How:
Reuse the same object time and again. All classes should have a init() or reset() or reinit() method (instead of a constructor) which would allow the re-use of the old object.
Don’t return objects from methods (because this likely requires the creation of the object to be returned), instead have the method take an out object parameter, which is passed in by the caller (who can reuse it) and is filled by the method with the result value.
Why: Performance gain by avoiding the object allocation overhead, and by reducing the garbage-collection overhead. Memory footprint reduction by decreasing the number of allocations.

#4: Don’t use packages. Put everything in the root package.

Why:
The package names incur some space overhead in the compiled classes.
Due to class-merging (#0), the package concept is anyway already irrelevant.
Having everything in a single package allows global access to package-visible methods and fields, thus enabling technique #5a below.

#5: Avoid method calls.

Why: Performance gain by avoiding the method call overhead. Also see #1, Define as few methods as possible.

#5a: Avoid accessor methods (getters, setters). Access directly the data members.

How: having everything in a single package (#4) allows access to all the members except the private ones. I.e., you don’t have to make a data member public
just because you want to access it, package-visibility is enough.

#5b: Inline small methods.

Why: avoids call overhead and reduces the number of methods.
How: use a preprocessor and preprocessor macros for the inlining. As the Java language doesn’t have a default preprocessor, you have the liberty to use any preprocessor you like. For example you may use CPP (‘C Pre Processor’) from GCC, but there are plenty of other choices.
Note: don’t trust the compiler or the virtual machine to inline your small methods (#7).

#6: Don’t use String. Use char array instead.

How: have a large char array, and use a pair of indices (begin, end) inside the array to delimitate your string.
Why:
Reduces the number of object creations and method calls. A char array (with a pair of indices) allows you to extract substrings without object creation,
and to iterate over the characters without the overhead of a method call for each character (String.getAt()).
Also enables a method to return a string without requiring object instantiation (by passing the char array as an out parameter to the method).

#7: Don’t trust the compiler or VM with the optimizations.

Why: the only optimizations you can rely on are the ones that you explicitly code yourself. Conservatively consider the compiler and the VM as dummy as possible.
Bad:


doSomething(2*n, 2*n+1);

Good:


int doubleN = n << 1;
doSomething(doubleN, doubleN + 1);

#8: Avoid array of objects, use multiple elementary arrays instead.

This is best described by an example. Let's say you need an array of points, where a point is a pair of two integer coordinates (x, y).
Instead of the first-shot solution:


static final int MAX_POINTS = 100;
class Point {
  int x, y;
}
Point myPoints[] = new Point[MAX_POINTS];
{
  for (int i = 0; i < MAX_POINTS; ++i) {
    myPoints[i] = new Point();
  }
}

Use the much-improved solution:


int Xs[] = new int[MAX_POINTS];
int Ys[] = new int[MAX_POINTS];

Why: you gain one less class, and a lot of memory footprint from avoiding the many object instantiations.

#9: Use the all-static singleton.

Every time you have a class that will have a single instance, you should take the opportunity to use the singleton pattern.
The preferred way of implementing the singleton is to declare all the fields (methods and data) static. This avoids the need to allocate an instance of the singleton class (saves memory), and allows for the class-merging of the all-static singleton with any other (non-singleton) class, thus saving one more class definition (#0).

#10: Avoid exceptions.

Why: The try/catch block incurs a space overhead (in the compiled class). There is also a performance penalty when an exception is thrown/caught.
How: Don't throw. Don't catch. Don't define new exception classes (#0), use the standard Error or RuntimeException instead (these are unchecked exceptions, so you don't have to catch them) if you really have to throw at times.

Conclusion

The name Assembly Java makes reference to Assembly Language, which too is renowned for its outstanding performance and ressource efficency.

So these are the 10 principles of Assembly Java (#0 - #10 above), use them well.

Update 2006-11-30: there is a follow-up to this article, called 'Mobile Java', that you might want to read as well.

{ 12 } Comments

  1. Anders Borg | 2006-11-27 at 17:02 | Permalink

    Seems very much like how I code MIDP applications, more for practical reasons than for performance ditto. I’m an old C programmer, so some optimizations come automatically.

    The only things I haven’t used are #6 (I use StringBuffer though), #9 and #10. In many cases I could have used #9.

    #0: I’ve found this impractical as I need to make reusable “black boxes” for different features, like a camera class, an RMS class etc. Otherwise I would spend time developing stuff all over again. Hence, there’s another aspect of programming: programmer performance, not just application performance. This way I’ve been able to throw together applications in hours instead of in days.

    #10 doesn’t seem altogether right. How do you then handle e.g. intermittent connectivity issues?

  2. Marc godin | 2006-11-28 at 10:54 | Permalink

    Nice to see your blog
    Mihai
    regards

  3. mihai | 2006-11-28 at 11:53 | Permalink

    Enrique, thank you and I’m honored to find my article linked on your blog. I was surprised and a bit embarassed to have my post exposed in the public, when I was thinking that it’s only me and my wife who’s reading my blog :)
    So I was quick to post a disclaimer.

    Anders, your observations are right. The ‘guidelines’ were an intentional exageration of the normal usage. Yes, self-contained classes are good for reuse (I do this too), and StringBuffer to avoid instantiation is less extreme (and easier to use) than a char array. The all-static singleton is likely a bad idea. And exceptions are unavoidable, but should be used with measure.

  4. Cedric | 2006-12-06 at 21:49 | Permalink

    Would have been fine advice five years ago, now most of these points are moot (and those that are not are taken care of by the obfuscator anyway).

    Premature optimization is still the root of all evil…


    Cedric

  5. Javi.NET | 2006-12-07 at 11:54 | Permalink

    When you have to develop a real time communications, event oriented, with more than 20k code lines, using only a code populated of Jappo macros so with only a click in a selected ant file you can generate and compile the same code for four different j2me platforms (each one with different profiles)… and do it while you keep the code legibility because protocols upgrades are usual, a easier code maintenance, and highly reliability… you can only remember this principles as a far echo in your head.

    Sorry, but give me proguard instead now!

  6. Antonio Terreno | 2006-12-13 at 11:31 | Permalink

    This post is pure rubbish!!!
    What the hell are the obfuscators/optimizers for?
    Better to have rubbish not maintainable code or maintainable code?

  7. Gary | 2006-12-13 at 11:57 | Permalink

    You’ve certainly got the right approach to writing leaner, faster code, but you’re missing the point with Java. It’s supposed to be maintainable! I’ve been an assembler programmer for over 20 years so I can agree with what you’re saying, but if you’re going to do this kind of stuff in assembler, make sure you don’t do it at the expense of losing maintainability. I can guarantee you that processor speeds will eventually catch up.

  8. Amir | 2007-01-09 at 19:04 | Permalink

    Good work Mihai.

    On some of the many smaller handsets that we still have to support (our build list covers over 500 different j2me devices) then most of this post makes absolute sense.

    Our apps have to run on very very slow, 64kj devices with less than 200k heap. You’re fighting for every byte on these devices, you better understand these concepts. And we’ll be supporting these devices for at least another 12 months.

  9. Marco | 2008-07-06 at 11:53 | Permalink

    All still highly relevant if you’re trying for as much performance as possible.

    I’m writing a 3d game using mascot capsule, and even now in 2008 the hit from newing and method calls is enormous. As a result I performed many of the optimizations similar to the ones Mihai talks about and it’s over doubled the game’s speed.

  10. Richeve Bebedor | 2010-01-29 at 17:27 | Permalink

    these are good tips for mobile applications only but i appreciate it so much because for me, as a non mobile app programmer, code optimization is a very big consideration. this is a great help! thank you for having these principles XD

  11. Richeve Bebedor | 2010-01-29 at 17:32 | Permalink

    Added to my post i want to encourage this part because little things are sure to be forgotten XD what we want to achieve as a programmer is a program with fewer codes, maintainable and optimized to achieve that we need to explore. it is just a matter of combinations. i am saying as a programmer with a beginner’s point of view thank you XD

  12. Neil Harding | 2010-02-01 at 21:28 | Permalink

    For the absolute best case in optimization for case 2, you should use -1,0,1 for the values then you can do

    class Animal {
    static final int DOG=-1, CAT=1, MOUSE = 0;
    String saySomething(int kind) {
    if (kind 0) return “meow”;
    return “squeak”;
    }
    }

    this also allows you to do DOG | CAT is != 0
    CAT | MOUSE >= 0 etc

    If you have a lot of cases then by grouping related ones into negative / positive you can use 0. The Java compiler is like the old C compilers back in the ’80s before any of the optimizations that were made (some C++ compilers can generate code that is almost as good as assembly, but since Java is already slow on Mobiles and size limited it is worth optimizing the code rather than letting the compiler, although a tool such as proguard means you can use more maintainable code, but it won’t solve all your problems since it doesn’t refactor the logic, just the generated code).

{ 3 } Trackbacks

  1. The 10 principles of Assembly Java…

    A good compilation of tips by Mihai Preda’s of the The anatomy of a project weblog. These are tips to consider to help keep your Java ME code small. I have used many of them, and some of these tips and others can be found on Chapter 10 – Techniques f…

  2. [...] It’s a long time that I am in the mobile industry, before J2me, and at a time where cell phone where not a mass market product!I’ve followed the evolution of mobile development practices, especially in Java. So I agree partially with this article: The 10 principles of Assembly Java who gives good hint and tips for mobile development, but I also think that hopefully most of them will start to disappear slowly.It’s a really strange industry, where on one hand people are talking of “MobileAjax” as the killer app, where the cost of one Ajax line is probably equivalent to the cost (in terms of CPU, memory used,battery) of a full Midp1.0 application, while other are strugling with putting everything in a single class to fit in constrained devices. [...]

  3. [...] The 10 principles of Assembly Java (tags: mobile java j2me software development) [...]

Post a Comment

Your email is never published nor shared. Required fields are marked *