|
|
Home |
Making Simulations Run Faster |
Occasionally I
run long simulations of big
networks (up to 1000 nodes), which send a lot of messages. These long
simulations could take hours and even days to finish (sometimes they were
practically unfinishable).
Thus, I have used
JProfiler to identify the functions that run the majority of the time and
the objects that are mostly allocated. After finding various un-optimized code
pieces and optimizing them, my simulations run an order of magnitude faster
- instead of days it takes hours (I have to mention that I needed to
optimize only my code - the original JiST/SWANS is already optimized at a very
good level).
I will share now a number of optimizations I made, since I believe them to be a common case for many Java programs
High overhead of unnecessary logging and assertion code:
The most time-consuming function in my code was StringBuffer.append()
(called upon string concatenation) and the mostly allocated object was
String.
My code is abundant with debugging places: every sent/received message was passed to a special printDebug(String) function, serialized into a string, concatenated with sender id , destination id, etc... and printed to System.out. I had a DEBUG switch to prevent the actual I/O operation, which was turned off during the actual runs, BUT the switch was inside the printDebug() function, protecting only the System.out.println() instruction, and the message serialization took place anyway.
I use A LOT of asserts in my code. The assert function receives some boolean condition and a string to be printed if the condition is false. Of course, during the actual run asserts did not fail, BUT the string parameter was sent to it anyway. A lot of times the string was actually a serialized message or a concatenation of various parameters. So all this string serialization/concatenation was in vain and consumed a lot of process resources. Instead, one can now use the new java assert booleanExpression : errorMessage; utility. It does the errorMessage evaluation only if the boolean expression is false.
In AODV code
SWANS uses printlnDebug() function. There is a RouteAodv.DEBUG_MODE flag inside
this function, but a lot of times concatenated strings are passed to it, which
is the same case as a. and b. So the solution is to put
if (RouteAodv.DEBUG_MODE) outside the
places that call printlnDebug(), to prevent the call itself.
Be
careful when using Java Collections - avoid memory leakage:
Java (actually any language with garbage collector) CAN have a floating garbage,
which is not collected by the garbage collector. I mean the "logical
garbage" - if you don't use certain object but accidentally keep some
reference to it. This mainly happens when putting some items in HashMap/Hashtable/Set/Vector/Array
or what ever collection. You put an object there, but forget to remove it
after the usage. The garbage gets accumulated until the memory explodes
(it could also cause to swapping, which slows the program
significantly).
This phenomenon can be seen in various ways:
a) In JProfiler virtual memory size constantly increases.
b) In your program you can occasionally print Runtime.getRuntime().totalMemory()
and see the memory growing.
c) In JProfiler one of the mostly allocated objects was HashMap.Entry.
In my case case, all those
waists were due to the fact that every message that was sent, was put in some
HashMap for a statistical analysis at the end, but it caused my program's
memory to explode.
Another example is an implementation of a flooding protocol - a node
sends a message which is retransmitted by any other node that receives it for
the first time. So every node has to remember the id of every message it
received in order not to retransmit it again. A not careful implementation
will store an Integer object for every message received by every node.
Imagine 1000 nodes network, every node starting 1000 floodings. It will result
in 1000*1000*1000 Integer objects, which is 1GB of memory.
The solution could be to store only some last K message identifiers from every
sender. Or simply occasionally garbage collect ids of message that were sent
far in the past and thus there is no chance to receive the same messages again.
So be careful when using Collections!
I hope you will find those tips useful. They are not any special "wisdom", just an experience I had.
Gabriel Kliot
11/07/2008