Page 1 of 1

Tip: Cache your constant tables

Posted: Tue Dec 11, 2018 5:56 am
by DaveMcW
I got a 10% speed boost in Recursive Blueprints by replacing this:

Code: Select all

function get_construction_signal(network)
  return network.get_signal({type="item", name="construction-robot"})
end
... with this:

Code: Select all

local CONSTRUCTION_SIGNAL = {type="item", name="construction-robot"}
function get_construction_signal(network)
  return network.get_signal(CONSTRUCTION_SIGNAL)
end
The one drawback is that lua does not enforce cached tables being constant, so you can break things by accidentally modifying the table. This is not a big problem in control.lua, but it might be an issue in data.lua.

Re: Tip: Cache your constant tables

Posted: Sat Feb 02, 2019 4:23 pm
by QGamer
(Sorry, I'm not super experienced with Lua.)
By cache you mean put them as local variables in Control.lua, right?
What types of values should I cache? Just tables, or would it help to cache other types as well?

Re: Tip: Cache your constant tables

Posted: Sat Feb 02, 2019 6:17 pm
by quyxkh
From `utils/consolegoodies.lua` in my own utilities library, implementing this:

Code: Select all


local foundsigs={}
function sig(id,c)
    -- return a spec for the given signal id and (optional) count. The
    -- signal type is found in prototypes, omitted count returns just
    -- the id, so
    --     sig'signal-black' → {name='signal-black',type='virtual'}
    -- and   sig('coal',100} → {count=100,signal=sig'coal'}  (further expanded).
    -- for extra fun, the `signal-` prefix is made optional, so
    --     sig'W' → {name='signal-W',type='virtual'}

    if c then return {count=c,signal=sig(id)} end
    if not foundsigs[id] then
	foundsigs[id] = game.virtual_signal_prototypes[id] and	{name=id,type='virtual'}
		     or game.item_prototypes[id] and  {name=id,type='item'}
		     or game.fluid_prototypes[id] and  {name=id,type='fluid'}
		     or game.virtual_signal_prototypes['signal-'..id]
					    and  {name='signal-'..id,type='virtual'}
		     or {name='**unknown signal id "'..id..'"'}
	local _, _2, short = foundsigs[id].name:find'^signal%-(.*)'
	if _ then
	    foundsigs[foundsigs[id].name] = foundsigs[id] -- with signal-
	    foundsigs[short]              = foundsigs[id] -- and without it
	    end
        end
    return foundsigs[id]
    end
edit: @QGamer, literal tables cause allocation when they're encountered, it's cheap but not nearly as cheap as not doing it, keeping previously-used tables around if they'll serve as well. So I'll do things like `local empty = {}`. Numbers and strings don't cause allocation, tables and functions do, that much I'm sure of.

Re: Tip: Cache your constant tables

Posted: Sat Feb 02, 2019 6:45 pm
by eduran
quyxkh wrote:
Sat Feb 02, 2019 6:17 pm
So I'll do things like `local empty = {}`.
Can you elaborate when and how this would be useful?

Re: Tip: Cache your constant tables

Posted: Sat Feb 02, 2019 7:29 pm
by quyxkh
eduran wrote:
Sat Feb 02, 2019 6:45 pm
quyxkh wrote:
Sat Feb 02, 2019 6:17 pm
So I'll do things like `local empty = {}`.
Can you elaborate when and how this would be useful?
Sure: any time your code passes through a `{` lua makes a new table. If instead of writing `{}` for an empty table you say `local empty = {}` and use `empty`, it's faster—by, as DaveMcW's example shows, more than just a little. So for instance search functions will often return `nil` if they don't find anything, but you can't index `nil`. The usual code taught to beginners to deal with that is bulky and repetitive, boilerplate. Instead of a big separate if statement it's often better to say e.g. `for k,v in pairs(train.get_fluid_contents() or empty)` or `(s.find_entity('rail-signal',pos) or empty).direction` or the like. My consolegoodies code puts a metatable on its empty so I don't unthinkingly stomp on it, `local empty = setmetatable({},{__newindex = function() error 'assigning to empty' end}), I don't usually bother with that in loaded code though.
`

With some nontrivial discipline caching like this even lets you start comparing tables by identity not value, if (using the above code) your main namespace has e.g. `sigW=sig'W'` in it and everybody's going through `sig` for their mappings, you can say `if insig==sigW` for the kind of speed boost that can snowball if you work at it. But this is the kind of thing that can and will bite the unwary, causing endless pain and damage to the unwise and their victims, so heed the warning.

Re: Tip: Cache your constant tables

Posted: Sun Feb 03, 2019 9:49 am
by Optera
Caching tables only works if they are either read only or used in only one persistent instance, like quyxkh's example with foundsigs.
Having an empty table speed up initializing tables will not work in many cases as lua always references tables.

Code: Select all

local empty = {}

function doSomething()
  local a = empty
  a[1] = "whatever"
  local b = empty
  log (serpent.block(b) ) => {[1] = "whatever"}
end