Very slow on Android

Monkey Targets Forums/Android/Very slow on Android

siread(Posted 2012) [#1]
So I managed to get my app running on an Android phone and whilst the action sections run smoothly enough the loading and processing of data is unbelievably slow. For instance, when setting up my football fixtures I get wait times like this...

Flash & HTML: 1 second
iPad 2: 3 secs
iPhone 3GS: 11 secs
San Fran II: 115 secs (Android 2.3.5, 800mhz, 512 ram)

Pound for pound the Android phone has better specs than the iPhone but is 10 times slower when the processor has to grind out results (as I say the action sections are fine). Is this normal? It feels as though the game is running in debug mode but I set the build option to release. Am I missing something?


slenkar(Posted 2012) [#2]
r u creating loadsa garbage?


siread(Posted 2012) [#3]
I do create around 3,000 fixture objects that are stored in arrays. So you think it is a GC issue?


ziggy(Posted 2012) [#4]
Are you sure you did not generate a debug build? It looks like a GC issue to me too. Declaring an array seems to alocate an object and this can feed the GC. Reuse them as much as possible...


siread(Posted 2012) [#5]
Aha! When I remove the command to save the game data the fixtures are processed in less than a second, so it's nothing to with the object creation.

Here's how the game data is saved...
Method SaveCareer()
	Local savestr:String
	
	' Global data
	savestr=GetGlobalData()+";"
	
	' Player data
	savestr+=GetPlayerData()+";"
	
	' Club data
	savestr+=GetClubData()+";"
	
	' Competition data
	savestr+=GetCompetitionData()+";"
	savestr+="<END>"
	
	SaveState(savestr)
End

' An example of the data that is being saved
Method GetCompetitionData:String()
	Local savestr:String
		
	For Local c:TCompetition = EachIn TCompetition.glist
		' Store comp id
		savestr+=c.id+","
		
		' Then save teampool
		For Local tp:TTeamPool = EachIn c.teampool
			For Local td:TTableData = EachIn tp.pool
				savestr+=td.GetWriteData()
			Next
			savestr+="*"
		Next
		
		savestr+=","
		
		' Then save fixtures
		For Local f:TFixture = EachIn c.lfixturelist
			savestr+=f.GetWriteData()
		Next
		
		savestr+="@"
	Next
	
	Return savestr
End

Class TFixture
	Method GetWriteData:String()
		Local str:String
		str+=sdate+"/"
		str+=matchtype+"/"
		str+=round+"/"
		str+=groupno+"/"
		str+=leg+"/"
		str+=hometeam+"/"
		str+=awayteam+"/"
		str+=result+"/"
		str+=resulttype+"/"
		str+=score1+"/"
		str+=score2+"/"
		str+=penscore1+"/"
		str+=penscore2+"/"
		str+=level+"/"
		str+=compid+"?"
		
		Return str
	End
End


Parsing all of the data from LoadState is pretty quick, so it's just the creation/storing of the string that takes time (2 mins for less than a MB!). What's going on?

Edit: I just tested it again but only removed the SaveState(savestr) command and it still took 2 mins to create the savestr. So it's nothing to do with storing the data but rather the concatenation of one large string.


slenkar(Posted 2012) [#6]
monkey has its own string class, I think even in Java

Maybe the string implementation isnt done in the most efficient manner?


DruggedBunny(Posted 2012) [#7]
Do any of the individual GetXXXData () calls take longer than the others? How long does it take if you just call GetCompetitionData, for example, since we can see what's in that?


AdamRedwoods(Posted 2012) [#8]

Maybe the string implementation isnt done in the most efficient manner?


correct, it's not.

monkey strings are immutable, so a new string is created EVERY time you add something new to the old string. That's a LOT of work to do and a lot of object creation.

How to get around this? That i have no idea.
- Try adding ALL values into one line:
Local savestr:String
	
	' Global data
	savestr=GetGlobalData()+";"+GetPlayerData()+";"+GetClubData()+";"+GetCompetitionData()+";"+"<END>"
	
	SaveState(savestr)


- my other thought would be to use a string array instead of one large string in your EachIn tppool loop. THAT is the culprit!


DruggedBunny(Posted 2012) [#9]
If that's the case, String.Join might help (if you can put the strings into an array rather than a list, perhaps resizing it when necessary). String.Join looks like this in lang.java...

static public String join( String sep,String[] bits ){
	StringBuffer buf=new StringBuffer();
	for( int i=0;i<bits.length;++i ){
		if( i>0 ) buf.append( sep );
		buf.append( bits[i] );
	}
	return buf.toString();
}


A quick Google shows that StringBuffer is mutable, and it looks like it'd all get done a lot quicker. (It would appear that buf.append is just a case of more memory being allocated, not new objects.)


DruggedBunny(Posted 2012) [#10]
Yep, String.Join makes a MASSIVE difference over manually joining strings together...



Run this for STDCPP (you may need to change the SaveString filepath at the bottom of this code if on Mac, etc, if you want to check the output); it takes around 10 seconds for me with USE_JOIN set to False.

Then run it with USE_JOIN set to True... it's almost instant!

I'd imagine something like this to try and minimise the work needed:

Method GetCompetitionData:String()
	Local savestr:String [REASONABLE_NUMBER, or count list items first!]


... then, rather than...

savestr+=td.GetWriteData()


... something like:

savestr[index]=td.GetWriteData() ' For each savestr+=whatever
index = index + 1

' If not pre-calc'ing array size...

If index > savestr.Length - 1 Then savestr = savestr.Resize [savestr.Length * 2] ' Double it!


Join at the end of the method and return the string.

For GetWriteData, it'd be something like this:

	Method GetWriteData:String()
		Local str:String [15]
		str[0]=sdate+"/"
		str[1]=matchtype+"/"

		... etc...

		Local result:String = result.Join [str]
		Return result



marksibly(Posted 2012) [#11]
Hi,

Monkey does use java strings, this is really just a 'big O' problem.

If you are concatenating many strings together, try using a StringStack as a temporary buffer, eg:

Local buf:=New StringStack
buf.Push "Blah"
buf.Push "Etc"
...etc...
Local result:=buf.Join( "" )



siread(Posted 2012) [#12]
@James
That method has made a massive improvement! Got the saving time to around 25 seconds and there are probably a few other areas that I can sharpen up. Cheers!

@Mark
Thanks. I will try using a StringStack in the morning and see how it compares to James' method. :)


DruggedBunny(Posted 2012) [#13]
Excellent, though 25 seconds is still a long time. StringStack does look easier!


siread(Posted 2012) [#14]
Yep, StringStack is even faster. Down to 4 seconds! Cheers guys.


Samah(Posted 2012) [#15]
Mark,
Can we get a constructor for String that takes an Int array? Basically it would just do String.FromChar on each of the elements and concatenate them without creating extra String objects for each one. Similar to Java's String(char[]) constructor. Going the other way with a String.ToCharArray would be nice too.

Edit: Like the BuildString function in Diddy:
http://code.google.com/p/diddy/source/browse/trunk/src/diddy/functions.monkey
It's externed in Java, but implemented in Monkey for other targets.


DruggedBunny(Posted 2012) [#16]

Yep, StringStack is even faster. Down to 4 seconds! Cheers guys.


Brilliant result!