Corona code optimisation

To easily verify what davebollinger said just replace your long string with something short but unique for each entry - like

c[i][j] = “teststring”…(i*1000+j)

and watch your memory requirements become even more impressive :slight_smile:

There are a few more rules/tips to optimize memory usage. F.i. the overhead for tables is bigger than for values which means, if you use a 1d array instead of 2d or even 3d, there’s some more memory to be saved at slightly more complex, but maybe even faster code especially while iterating/updating your tiles.

I.e. you’d write

local mapWidth = 130 local mapHeight = 130 local mapArray = { filled with your content } for iy = 1, mapHeight do     local tileIdx = iy\*mapWidth     for ix = 1, mapWidth do         local tileValue = mapArray[tileIdx]         tileIdx = tileIdx + 1     end end

The reason this can be better is because Lua does not support multidimensional arrays in a single memory block as f.i. C/C++, you just create lots of small arrays within other arrays and if you do 2 or even 3 dimensions you actually do just a second or third array access which for sure is slower than a simple addition.

Downside is, single element access is a bit less convenient.

Also, there is a chance that, depending on how you build your arrays, Lua might use it’s hash table implementation instead of the direct integer based indexing but I’d start with big low hanging fruit first (i.e. the boolean arrays).

I did try and ran out of memory fast!

I’ve merged all my boolean lookup arrays into a single array called  tileStats.  This now stores 10 values per index (ints and booleans) and saved around 5Mb.  I have a much larger and more complicated array called  buildings that stores 100+ values, displayGroups, etc. per index.  I find looking up all my main run-time values in tileStats is much faster than accessing the main array if I just want to know if there is say a road asset at x,y rather than the road piece, index, rotation, how it connects to its neighbours, etc.

Just getting your valued input here really… I often do things like this (which may not be the most optimised but it is easy to code and more importantly easy to read

function rebuild\_isTileARoad() local function rebuild() --add reference to whether this tile has a road or path asset local x, y = 0, 0 for x = 1, \_maxTilesX do for y = 1, \_maxTilesY do tileStats[x][y].isRoad = buildings[x][y].isRoad or buildings[x][y].isPath end end end if \_debug then rebuild() else pcall(rebuild) end end 

and then repeatedly query it throughout the lifetime of my app like this

if not tileStats[x][y].isRoad then --allow action else --block action end

Would you guys do anything different?

@Michael Flad In this sort of situation where one is only dealing with a bit at a time, you can even just emulate the bitwise ops and that gets you up to 53 bits (after which the spacing between consecutive double-precision numbers goes from 1 to 2 and you can no longer hit integers reliably). I wrote a bit vector along these lines some time back. Of course with userdata you can go to 64 and beyond.

As for an “ints and booleans” case such as  tileStats , I’ve also had my eye on this project for quite some time now, though I don’t know how difficult it would be to port to ARM.

@adrianmm If a lot of your information is “cold” (only used occasionally), especially if a lot of it happens to be small integers, the MemoryBlob plugin I recently released might help. (You can find sample code linked in the docs.) This would let you keep some data together without the slight per-object Lua bookkeeping overhead, paying in retrieval time instead. At the moment this probably requires decomposing integers into bytes and some slightly cumbersome string.byte and string.char calls, but if all goes well I should have another plugin out shortly that wraps up some data structuring APIs. (Both of these are / will be free.)

Slightly related, I’ve been considering binding bundle to allow for in-memory compression, which I think might be interesting in scenarios like these.

Basically my issue is not knowing how much RAM is available on the device.  I’ve searched the docs but can find no way of actually retrieving this valuable piece of info using system.getInfo() or similar.  I could approximate this based on DPI but that doesn’t work as a tablet my have a low DPI but way more RAM than the equivalent phone based on DPI alone.

Whilst knowing how much RAM my app is using is useful, it is essentially meaningless without knowing how much is available on the device.  200MB is nothing if you have 2GB available… it is a massive issue if the device only has 512MB.

According to stackoverflow this is relatively trivial.  If someone could knock up a simple plugin to answer what is surely a clear hole in Corona that would be awesome!  It needs nothing more than a single function to return the total RAM and then developers could adapt their games accordingly.  Would be preferable that the SDK exposes this as it is really important for larger, more resource intensive apps. 

I would be interested in this too. Retrieving SQLlite data on Windows builds is so slow that I have to load as much into memory as possible and lookup against that. I may just have to stipulate a minimum of 2GB ram.

2GB minimum - wow!  Why do you need to store so much data?

For me personally, if I knew the player was launching my game (for the first time) on a device with under 1GB RAM I could size their city to 100x100 and those over 1GB get the full 130x130.  I can only do this on the very first launch so the process is transparent to the end user.

It’s an historical management sim, so there’s around 11,000 players depending which year you choose, each with a career up to 20 years long. It’s really the new game creation phase that is the issue, data on 100,000 player-seasons, 15,000 clubs and 279 nations really need to be in memory to have this perform at an acceptable speed on Windows machines with IDE drives. Unlike a normal management sim where their ability & attributes are already set, my game has to analyse each player’s career path to determine how good they were.

Do you calculate 20 years worth of data on first load?  As this is historical data, could that not be precalculated and stored?

I don’t know your game so this may be totally irrelevant: but lets say you choose a country (UK) and then choose a team (Spurs) could you not then calculate their dataset and store it.  For each club looked at you would calculate their stats in real time.  Eventually you will have the full dataset without the huge upfront calculation.

Happy to help on the SQL side if you need a second pair of eyes… I create transactional SQL DBs for my day job.  Currently I have a few clusters in different locations all syncing and crunching data in real time.

Another option might be to scale out the calculations on a platform like AWS as that will be considerably faster than a PC but I appreciate this might involve large data transfers.

It could possibly be pre-calculated and I have considered it, but I wanted to avoid every game being exactly the same - there’s an element of randomness to how and when players develop.

Another issue is that the base data is constantly being improved, having come from numerous sources. For example, I may discover that the Johnny Smith that played on the wing in Scotland from 1968-72 is the same John Smith that played at FB in England from 1974-1976. And he’s also the same Jon Smith that is in the international database with 4 caps in midfield for New Zealand. At the moment I just have to give them the same playerID and the data is combined automatically. By pre-calculating I’d have to re-calculate every time something changes.

Speed is not really an issue on iOS/OSX, as SQLlite is so much faster. So all this optimisation is aimed at getting Windows/Android up to the same speed.

For instance, there is a routine which picks the lineups for the CPU teams. Currently it selects the players from the DB for the given club, as well as grabbing their attributes ready for the match engine. I wrap the transactions up with ‘BEGIN TRANSACTION’ and ‘COMMIT’ but there’s still an overhead, compared with having all the players and their attributes in memory from the start.

So far, I have improved the process from taking 70 seconds for one league and no international database (on average this adds an extra 40% of players to create), to 25 seconds for four leagues including the international database, with the downside of memory usage going from a max of 70MB to around 380MB.

There are so many ways of optimising SQL that can massively improve performance, some are query-based, others index-based but the big gains come from re-structuring the physical data and normalisation.  Sometimes de-nomalising RI data can really speed up reads.

If you are running lots of inline aggregation then localising that into aggregated tables can have a massive performance gains.

A simplistic example… if you find your sim is constantly looping every game a team played in a season to find the goals scored you could have an aggregated table indexed per team per year and store the SUM().  This changes your query from a table scan (really slow) to an index seek (very fast).

Yeah, perhaps I need to look into this more. I’d love to be able to go back to just grabbing data when I need it, rather than having it in memory just in case. Just accessing the DB on Windows is so expensive, when I need a task such as picking a CPU side to be done in 10ms this is pretty much eaten up as soon as I go near SQLlite.