Finding Performance Bottlenecks: Titanium Mobile for iPad
Jun 20
iOS, ipad, Titanium Mobile titanium mobile No Comments
I’ve migrated this from the My Newspaper for iPad site as it is more relevant for us Titanium Developers that people who are following my product blog. I hope this article will get superseded once full debugging support is ready in Titanium Studio – Jeff, 3 June 2011.
No matter how you are developing in iOS, the performance in the iPhone / iPad Simulator differs greatly to the performance on the actual hardware. XCode has some great debugging tools for identifying where the differences are so you can fix them.
We are using Titanium Mobile to build My Newspaper. It has saved us tons of learning and development time, but the tools for debugging performance when running on the actual iPad hardware are lacking. We would notice things taking longer on the iPad, but what was it? The database? html rendering? image placement? We had no way of knowing.
Titanium has identified that they need to improve debugging in the next version of Titanium Developer and are working on a more complete debugging framework. We are looking forward the new framework, but it was pretty easy to ‘roll our own’ temporary framework to identify bottlenecks in My Newspaper.
We started by creating a new tracer.js file that we could include in both our js and html files. It is a simple class with only one method:
var TRACER_ENABLED = true;
function Tracer( myName ){
if( TRACER_ENABLED ){
var name = myName;
var startTime = (new Date()).valueOf();
}
this.trace = function( msg ){
if( TRACER_ENABLED ){
var dateTemp = new Date();
var elapsed = dateTemp.valueOf() - startTime;
Ti.API.debug( name + "\t" + startTime + "\t" + elapsed + "\t" + msg );
// fire an event to make the UI update
Ti.App.fireEvent( 'tracer.trace' , {
name : name,
id : startTime,
elapsed : elapsed,
message : msg
});
}
};
}
First, we set an overall variable TRACER_ENABLED that we can turn off for production. Not ideal to change code when deploying an app for the store, but we wanted to be able to have tracing on when distributing adhoc to some beta testers.
You invoke a new tracer using:
var myTracer = new Tracer('myname');
Setting a name helps you identify a filter in the UI later.
To trace a step, just use the trace method:
myTracer.trace('some message');
This will show the message as a debug message in the console with:
- the name of the tracer
- the time it started (you may have multiple tracers with the same name)
- time elapsed
- and the message
To see all the traces on the device when running, we added a UI in another JavaScript file: tracerui.js
var textarea;
var traces = [];
function loadTraces( chosenid ){
var tempString = "";
for( var i = traces.length - 1 ; i >= 0 ; i-- ){
var id = traces[i].name + " " + traces[i].id;
if( id == chosenid | chosenid == 'all'){
tempString += traces[i].name.lpad(" ", 12) + "\t" + traces[i].id + "\t" + traces[i].elapsed.toString().lpad(" ", 6) + "\t" + traces[i].message + "\n";
}
}
textarea.value = tempString;
}
if( TRACER_ENABLED ){
var winTracer = Titanium.UI.createWindow({
backgroundColor:'#fff',
title:"tracer"
});
var winTracerClose = Ti.UI.createButton({
title:'close'
});
winTracerClose.addEventListener('click' , function(){
winTracer.close();
});
winTracer.rightNavButton = winTracerClose;
winTracer.addEventListener('open', function(){
var pickerString = "all";
var pickerData = [];
// put on a row to show all
pickerData.push( Titanium.UI.createPickerRow({title:'all',selected:true}) );
for( var i = traces.length - 1 ; i >= 0 ; i-- ){
var id = traces[i].name + " " + traces[i].id;
if( pickerString.indexOf(id) == -1 ){
pickerString += "," + id;
pickerData.push( Titanium.UI.createPickerRow({title:id}));
}
}
var picker = Titanium.UI.createPicker({
top:0,
height:200
});
picker.add( pickerData );
picker.addEventListener('change' , function(e){
loadTraces(e.selectedValue[0]);
});
winTracer.add( picker );
textarea = Titanium.UI.createTextArea({
top:260,
borderStyle:Titanium.UI.INPUT_BORDERSTYLE_ROUNDED,
font:{fontFamily:'Courier',fontSize:10}
});
winTracer.add(textarea);
loadTraces( 'all');
});
Ti.App.addEventListener('tracer.trace' , function(e){
traces.push(e);
});
}
This is just a simple UI with a picker and a textarea to allow us to see all the traces or filter down to a specific named trace using the picker:
We keep all the traces in memory in the array ‘traces’. When the picker changes, we sort through them and put the results in a textarea.
Now we just need to add a button to open the tracer window somewhere in the app:
if( TRACER_ENABLED ){
var buttonTracerWindow = Ti.UI.createButton({
title:'trace',
left:500,
top:10,
width:45,
height:30
});
buttonTracerWindow.addEventListener('click' , function(){
winTracer.open({modal:true});
});
win.add( buttonTracerWindow );
if( TRACER_ENABLED ){
I realise this is very quick and dirty, but it has helped us identify what functions are fast on the iPad and what we will change. If you are looking for a quick, easy way to figure out what is slowing you down in the real world, this will remove any guessing.


