Is it worth using cross-references to avoid using pairs?

Sorry for the confusing title but basically I’m in a dilemma. So essentially I’m working on an object oriented wall cutter module, however there are some methods that require me to iterate over some of the tables’ contents, where these contents have indexes that are instances which means it doesn’t work with ipairs. So, my idea is to make a second table inside of my object that is just a table of instances that reference the indexes of the other table, thus creating a cross-reference (contents from one table referencing contents from another). Does the benefit of using cross-references outweigh the cons in this instance?

So for example,

function module.new()
    local self = {}
    self.Windows = {} :: { [Model]: { -- see here how the model is the index of the table
			LeftParts: {};
			RightParts: {};
			TopParts: {};
			BottomParts: {}
		}
    }
    -- so my idea is to make a table inside of the object like so:
    self._ref = {} :: {Model} -- a table of models who're being used as indexes in the self.Windows table above to be able to use ipairs instead of pairs
    return setmetatable(self, module)
end

So then to iterate through it, instead of doing

for i,windowInfo in pairs(self.Windows) do
    -- something for each window

I would do

for i,v in ipairs(self._ref) do
    local windowInfo = self.Windows[v]

to use ipairs since, once again, ipairs is faster than pairs. There are pros and cons of each:

Method that allows for ipairs:
Pros:

  • Faster

Cons:

  • A bit complicated
  • Memory usage is higher

Method that allows for pairs:
Pros:

  • Easier to look at
  • Reduced memory usage

Cons:

  • Slower

Any thoughts/alternative approaches?

I think

for i,windowInfo in pairs(self.Windows) do
    -- something for each window

is more or less the same as

for i,v in ipairs(self._ref) do
    local windowInfo = self.Windows[v]

performance wise


You could do a speed test to see if it’s true

1 Like

Yeah, just did a test with 1000 iterations, ipairs was only negligibly faster.

Benchmark
local obj1 = {}
obj1.Windows = {}

local obj2 = {}
obj2.Windows = {}
obj2._ref = {}

for i = 1,1000 do
	local m = Instance.new('Model')
	obj1.Windows[m] = {
		LeftParts = {};
		RightParts = {};
		TopParts = {};
		BottomParts = {}
	}
	obj2.Windows[m] = {
		LeftParts = {};
		RightParts = {};
		TopParts = {};
		BottomParts = {}
	}
	table.insert(obj2._ref, m)
end

local timeForPairs = os.clock()
for window, windowInfo in pairs(obj1.Windows) do
	table.insert(windowInfo.LeftParts, 1)
end
local endTimeForPairs = os.clock()
print('Pairs took', endTimeForPairs - timeForPairs)

local timeForIpairs = os.clock()
for i, window in ipairs(obj2._ref) do
	local windowInfo = obj2.Windows[window]
	table.insert(windowInfo.LeftParts, 1)
end
local endTimeForIpairs = os.clock()
print('Ipairs took', endTimeForIpairs - timeForIpairs)