Reputation: 11
I recently started writing a 2D application for Android (pure Java code). I don't have any considerable experience with Java and Android programming.
I'm getting really poor performance of my java code. It seems really strange as the phone i'm running is a Samsung Glaxy S2 (ARM Cortex A9 1.2Ghz Dual Core) whitch should give alot better results.
My application is a Live Wallpaper. I'm using android.graphics library to draw my images. I'm running a custom scene graph code to manage my transformations and bounding rect hierarchy (i'm allso doing visibility testing).
According to the DDMS profiler (method trace) my render loop executes for 30 - 45ms (not sure why it varies so much as my scene does not change from capture to capture).
The bitmap drawing takes around 15ms. The remaining time (15 - 30ms) is spend traversing the scene graph to update transformations and check for visibility.
I have 12 nodes and 11 drawable objects in the scene graph!
I really can't understand why the code is running so slow. From DDMS i can't identify any particular part of the code that is a bottleneck. I don't have any temporary objects allocated in the code except a ton of iterators that i can't figure out a way to get rid of.
It seems like just calling the methods takes ALOT of time. 30ms is alot for traversing 12 nodes and multiplying their matrices together + extending some rects.
1.6ms for sorting 4-5 objects???
I must be doing something really stupid! Any suggestions would be highly appreciated!
private final LinkedList<SceneNode> m_children = new LinkedList<SceneNode>();
private final LinkedList<Drawable> m_drawables = new LinkedList<Drawable>();
public void update(boolean updateChildren)
{
updateDerivedTransform();
if (updateChildren)
{
for (SceneNode child : m_children)
{
child.update(updateChildren);
}
}
updateBoundingRect();
}
protected void updateDerivedTransform()
{
if (m_parent != null)
{
m_derivedTransform.setConcat(m_parent.m_derivedTransform, m_transform);
}
else
{
m_derivedTransform.set(m_transform);
}
}
public void updateBoundingRect()
{
m_boundingRect.setEmpty();
for (Drawable drawable : m_drawables)
{
m_boundingRect.union(drawable.getTransformedBoundingRect());
}
for (SceneNode child : m_children)
{
m_boundingRect.union(child.m_boundingRect);
}
}
public void findVisibleObjects(RectF viewRect, DrawQueue queue, boolean includeChildren, boolean addNodes)
{
float x = m_boundingRect.left;
float y = m_boundingRect.top;
float w = m_boundingRect.right;
float h = m_boundingRect.bottom;
if (viewRect.contains(x, y, w, h) || viewRect.intersects(x, y, w, h))
{
queue.getContainer().addAll(m_drawables);
for (SceneNode child : m_children)
{
child.findVisibleObjects(viewRect, queue, includeChildren, addNodes);
}
}
}
Thanks alot for your help!
Upvotes: 0
Views: 904
Reputation: 5314
There is a lot of per-call overhead on Java, especially Android's implementation of it (in dalvik). Try to reduce the number of calls you're making across classes especially.
As a minor optimization (when you get to that point), instead of using LinkedList
, consider using ArrayList
(or even a statically-sized array, if you don't add elements often) and do this for your loop:
final int count = m_drawables.size();
for (int i = 0; i < count; i++) {
Drawable drawable = m_drawables.get(i);
//...
}
Android's JIT is really bad at optimizing container iterator loops (for (Foo foo : fooContainer)
) and I've found pretty major speedups by converting them into traditional counted loops. If you look at the code disassembly, such loops actually turn into a HUGE number of method calls with a lot of object creation and destruction for the iterator. They're convenient for code savings but not for performance (although hopefully future improvements to Dalvik's JIT will help with that).
You might also find it faster to just draw the objects than to try to determine which objects are visible before drawing them.
Upvotes: 0
Reputation: 20319
Here are a few thoughts without seeing your code.
Do you have your logic split into two threads? You should have one thread that only draws the wallpaper in its current state. Then have this thread run as fast as possible so your frame rate is as high as possible. In a second thread you should handle all the logic that updates your wallpaper based upon how much time has passed since the application started.
This serves two purposes. It abstracts away the logic into two different sections making it easier to bug check and optimize. It will also mean that your frame rate will not drop if the computations get particularly heavy.
That said if we can see the code maybe we can point out points in the code that are particularly inefficient and could be improved upon.
Upvotes: 2
Reputation: 345
I have found bouncing between methods and if statements inside loops both cause performance issues on lower spec devices.
Upvotes: 0