Efficiently storing and iterating over Lua multi-level queue - performance

This is the add format:
AddToQueue( String identifier, function callfunc, int priority )
priority is guaranteed to be 0 to 4, with 0 being the highest priority. My current setup is this:
local tQueue = {
[0] = {},
[1] = {},
[2] = {},
[3] = {},
[4] = {}
}
function AddToQueue( sName, funcCallback, iPriority )
queue[iPriority or 0][sName] = funcCallback
end
function CallQueue()
for i = 0, 4 do
for _, func in pairs( queue[i] ) do
local retval = func()
if ( retval ~= nil ) then
return retval
end
end
end
end
This works, but I want to know if there's a better way to store and iterate the functions to prevent doing 5 pair loops every call. Thanks!

If you iterate your queue frequently, and new callbacks additions is infrequent, then you can just store everything in single table, sorting it each time you've added new callback.

Related

Table keys not sorting correctly

I have a table that looks like this
{
["slot1"] = {}
["slot2"] = {}
["slot3"] = {}
["slot4"] = {}
["slot5"] = {}
["slot6"] = {}
}
When I do a for k, v in pairs loop I want the keys to go from slot1- the last slot. At the moment when I do a loop the order is inconsistent, slot 5 comes first etc. What is the best way to do this?
also I did not design this table, and I cannot change how the keys look
You can write a simple custom iterator:
local tbl = {
["slot1"] = {},
["slot2"] = {},
["slot3"] = {},
["slot4"] = {},
["slot5"] = {},
["slot6"] = {}
}
function slots(tbl)
local i = 0
return function()
i = i + 1
if tbl["slot" .. i] ~= nil then
return i, tbl["slot" .. i]
end
end
end
for i, element in slots(tbl) do
print(i, element)
end
Output:
1 table: 0xd575f0
2 table: 0xd57720
3 table: 0xd57760
4 table: 0xd5aa40
5 table: 0xd5aa80
6 table: 0xd5aac0
Create a new table:
slot = {}
for k,v in pairs(original_table) do
local i=tonumber(k:match("%d+$"))
slot[i]=v
end
Lua table orders are undeterministic. see here what makes lua tables key order be undeterministic
For your table you can try this
local t = {
["slot1"] = {},
["slot2"] = {},
["slot3"] = {},
["slot4"] = {},
["slot5"] = {},
["slot6"] = {}
}
local slotNumber = 1
while(t['slot' .. slotNumber]) do
slot = t['slot' .. slotNumber]
-- do stuff with slot
slotNumber = slotNumber + 1
end
This method does not handle if the table skips a slot number.

parallel iteration in lua

I would like to for-loop through multiple tables in parallel in Lua. I could just do:
for i in range(#table1)
pprint(table1[i])
pprint(table2[i])
end
But I'd rather something like python's zip:
for elem1, elem2 in zip(table1, table2):
pprint(elem1)
pprint(elem2)
end
Is there such a thing in standard Lua (or at least in whatever comes packaged with torch?).
If you want something in Lua that's similar to some Python function, you should look at Penlight first. For this specific case there is the seq.zip function. It seems that Penlight is installed together with Torch, but you can also get it via LuaRocks (which again is bundled with at least one Torch distribution).
Anyway, the seq.zip function in Penlight only supports zipping two sequences. Here is a version that should behave more like Python's zip, i.e. allowing more (or less) than two sequences:
local zip
do
local unpack = table.unpack or unpack
local function zip_select( i, var1, ... )
if var1 then
return var1, select( i, var1, ... )
end
end
function zip( ... )
local iterators = { n=select( '#', ... ), ... }
for i = 1, iterators.n do
assert( type( iterators[i] ) == "table",
"you have to wrap the iterators in a table" )
if type( iterators[i][1] ) ~= "number" then
table.insert( iterators[i], 1, -1 )
end
end
return function()
local results = {}
for i = 1, iterators.n do
local it = iterators[i]
it[4], results[i] = zip_select( it[1], it[2]( it[3], it[4] ) )
if it[4] == nil then return nil end
end
return unpack( results, 1, iterators.n )
end
end
end
-- example code (assumes that this file is called "zip.lua"):
local t1 = { 2, 4, 6, 8, 10, 12, 14 }
local t2 = { "a", "b", "c", "d", "e", "f" }
for a, b, c in zip( {ipairs( t1 )}, {ipairs( t2 )}, {io.lines"zip.lua"} ) do
print( a, b, c )
end
--------------------------------------------------------------------------------
-- Python-like zip() iterator
--------------------------------------------------------------------------------
function zip(...)
local arrays, ans = {...}, {}
local index = 0
return
function()
index = index + 1
for i,t in ipairs(arrays) do
if type(t) == 'function' then ans[i] = t() else ans[i] = t[index] end
if ans[i] == nil then return end
end
return unpack(ans)
end
end
--------------------------------------------------------------------------------
-- Example use:
--------------------------------------------------------------------------------
a = {'a','b','c','d'}
b = {3,2,1}
c = {7,8,9,10,11}
for a,b,c,line in zip(a,b,c,io.lines(arg[0])) do
print(a,b,c,line)
end
print '\n--- Done! ---'
Made a version with variable number of lists based on the other responses and added coroutines, it's not the fastest but I think it's very readable. I'm gonna use it but maybe someone else also finds it usefull.
Also this was made for use with neovim's LuaJit.
local zipgen = function(args)
-- find smallest iterator's size
local min = #args[1]
for i = 2, #args, 1 do
min = #args[i] < min and #args[i] or min
end
-- create list with 'i'th element from all iterators
for i=1, min do
local ans = {}
for j=1, #args do
-- get 'j'th iterator's 'i'th element
ans[j] = args[j][i]
end
-- return list of 'i'th elements
coroutine.yield(unpack(ans))
end
end
-- python like zip iterator
zip = function(...)
local args = {...}
-- return a function that resumes the coroutine when called
return coroutine.wrap(function() zipgen(args) end)
end

How can I deep-compare 2 Lua tables, which may or may not have tables as keys?

(Also posted on the Lua mailing list)
So I've been writing deep-copy algorithms, and I wanna test them to see if they work the way I want them to. While I do have access to the original->copy map, I want a general-purpose deep-compare algorithm that must be able to compare table keys (tables as keys?).
My deep-copy algorithm(s) are avaliable here: https://gist.github.com/SoniEx2/fc5d3614614e4e3fe131 (it's not very organized, but there are 3 of them, one uses recursive calls, the other uses a todo table, and the other simulates a call stack (in a very ugly but 5.1-compatible way))
Recursive version:
local function deep(inp,copies)
if type(inp) ~= "table" then
return inp
end
local out = {}
copies = (type(copies) == "table") and copies or {}
copies[inp] = out -- use normal assignment so we use copies' metatable (if any)
for key,value in next,inp do -- skip metatables by using next directly
-- we want a copy of the key and the value
-- if one is not available on the copies table, we have to make one
-- we can't do normal assignment here because metatabled copies tables might set metatables
-- out[copies[key] or deep(key,copies)]=copies[value] or deep(value,copies)
rawset(out,copies[key] or deep(key,copies),copies[value] or deep(value,copies))
end
return out
end
Edit: I found things like this which don't really handle tables as keys: http://snippets.luacode.org/snippets/Deep_Comparison_of_Two_Values_3 (Copy of snippet below)
function deepcompare(t1,t2,ignore_mt)
local ty1 = type(t1)
local ty2 = type(t2)
if ty1 ~= ty2 then return false end
-- non-table types can be directly compared
if ty1 ~= 'table' and ty2 ~= 'table' then return t1 == t2 end
-- as well as tables which have the metamethod __eq
local mt = getmetatable(t1)
if not ignore_mt and mt and mt.__eq then return t1 == t2 end
for k1,v1 in pairs(t1) do
local v2 = t2[k1]
if v2 == nil or not deepcompare(v1,v2) then return false end
end
for k2,v2 in pairs(t2) do
local v1 = t1[k2]
if v1 == nil or not deepcompare(v1,v2) then return false end
end
return true
end
Serializing is also not an option, as order of serialization is "random".
As others said, that depends a lot on your definition of equivalence.
If you want this to be true:
local t1 = {[{}] = {1}, [{}] = {2}}
local t2 = {[{}] = {1}, [{}] = {2}}
assert( table_eq(t1, t2) )
If you do, then each time the key in t1 is a table, you'll have to
check its equivalence with every table key in t2 and try them one by
one. This is a way to do it (metatable stuff left out for readability).
function table_eq(table1, table2)
local avoid_loops = {}
local function recurse(t1, t2)
-- compare value types
if type(t1) ~= type(t2) then return false end
-- Base case: compare simple values
if type(t1) ~= "table" then return t1 == t2 end
-- Now, on to tables.
-- First, let's avoid looping forever.
if avoid_loops[t1] then return avoid_loops[t1] == t2 end
avoid_loops[t1] = t2
-- Copy keys from t2
local t2keys = {}
local t2tablekeys = {}
for k, _ in pairs(t2) do
if type(k) == "table" then table.insert(t2tablekeys, k) end
t2keys[k] = true
end
-- Let's iterate keys from t1
for k1, v1 in pairs(t1) do
local v2 = t2[k1]
if type(k1) == "table" then
-- if key is a table, we need to find an equivalent one.
local ok = false
for i, tk in ipairs(t2tablekeys) do
if table_eq(k1, tk) and recurse(v1, t2[tk]) then
table.remove(t2tablekeys, i)
t2keys[tk] = nil
ok = true
break
end
end
if not ok then return false end
else
-- t1 has a key which t2 doesn't have, fail.
if v2 == nil then return false end
t2keys[k1] = nil
if not recurse(v1, v2) then return false end
end
end
-- if t2 has a key which t1 doesn't have, fail.
if next(t2keys) then return false end
return true
end
return recurse(table1, table2)
end
assert( table_eq({}, {}) )
assert( table_eq({1,2,3}, {1,2,3}) )
assert( table_eq({1,2,3, foo = "fighters"}, {["foo"] = "fighters", 1,2,3}) )
assert( table_eq({{{}}}, {{{}}}) )
assert( table_eq({[{}] = {1}, [{}] = {2}}, {[{}] = {1}, [{}] = {2}}) )
assert( table_eq({a = 1, [{}] = {}}, {[{}] = {}, a = 1}) )
assert( table_eq({a = 1, [{}] = {1}, [{}] = {2}}, {[{}] = {2}, a = 1, [{}] = {1}}) )
assert( not table_eq({1,2,3,4}, {1,2,3}) )
assert( not table_eq({1,2,3, foo = "fighters"}, {["foo"] = "bar", 1,2,3}) )
assert( not table_eq({{{}}}, {{{{}}}}) )
assert( not table_eq({[{}] = {1}, [{}] = {2}}, {[{}] = {1}, [{}] = {2}, [{}] = {3}}) )
assert( not table_eq({[{}] = {1}, [{}] = {2}}, {[{}] = {1}, [{}] = {3}}) )
Instead of direct comparison you may try to serialize each of the tables and compare the serialized results. There are several serializers that handle table as keys and can serialize deep and recursive structures. For example, you may try Serpent (available as a standalone module and also included in Mobdebug):
local dump = pcall(require, 'serpent') and require('serpent').dump
or pcall(require, 'mobdebug') and require('mobdebug').dump
or error("can't find serpent or mobdebug")
local a = dump({a = 1, [{}] = {}})
local b = dump({[{}] = {}, a = 1})
print(a==b)
This returns true for me (both Lua 5.1 and Lua 5.2). One of the caveats is that the serialization result depends on the order in which keys are serialized. The tables as key by default are sorted based on their tostring value, which means that the order depends on their allocation address and it's not difficult to come up with an example that fails under Lua 5.2:
local dump = pcall(require, 'serpent') and require('serpent').dump
or pcall(require, 'mobdebug') and require('mobdebug').dump
or error("can't find serpent or mobdebug")
local a = dump({a = 1, [{}] = {1}, [{}] = {2}})
local b = dump({[{}] = {2}, a = 1, [{}] = {1}})
print(a==b) --<-- `false` under Lua 5.2
One way to protect against this is to consistently represent tables in keys comparison; for example, instead of default tostring, you may want to serialize tables and their values and sort the keys based on that (serpent allows a custom handler for sortkeys), which would make the sorting more robust and the serialized results more stable.

Using par map to increase performance

Below code runs a comparison of users and writes to file. I've removed some code to make it as concise as possible but speed is an issue also in this code :
import scala.collection.JavaConversions._
object writedata {
def getDistance(str1: String, str2: String) = {
val zipped = str1.zip(str2)
val numberOfEqualSequences = zipped.count(_ == ('1', '1')) * 2
val p = zipped.count(_ == ('1', '1')).toFloat * 2
val q = zipped.count(_ == ('1', '0')).toFloat * 2
val r = zipped.count(_ == ('0', '1')).toFloat * 2
val s = zipped.count(_ == ('0', '0')).toFloat * 2
(q + r) / (p + q + r)
} //> getDistance: (str1: String, str2: String)Float
case class UserObj(id: String, nCoordinate: String)
val userList = new java.util.ArrayList[UserObj] //> userList : java.util.ArrayList[writedata.UserObj] = []
for (a <- 1 to 100) {
userList.add(new UserObj("2", "101010"))
}
def using[A <: { def close(): Unit }, B](param: A)(f: A => B): B =
try { f(param) } finally { param.close() } //> using: [A <: AnyRef{def close(): Unit}, B](param: A)(f: A => B)B
def appendToFile(fileName: String, textData: String) =
using(new java.io.FileWriter(fileName, true)) {
fileWriter =>
using(new java.io.PrintWriter(fileWriter)) {
printWriter => printWriter.println(textData)
}
} //> appendToFile: (fileName: String, textData: String)Unit
var counter = 0; //> counter : Int = 0
for (xUser <- userList.par) {
userList.par.map(yUser => {
if (!xUser.id.isEmpty && !yUser.id.isEmpty)
synchronized {
appendToFile("c:\\data-files\\test.txt", getDistance(xUser.nCoordinate , yUser.nCoordinate).toString)
}
})
}
}
The above code was previously an imperative solution, so the .par functionality was within an inner and outer loop. I'm attempting to convert it to a more functional implementation while also taking advantage of Scala's parallel collections framework.
In this example the data set size is 10 but in the code im working on
the size is 8000 which translates to 64'000'000 comparisons. I'm
using a synchronized block so that multiple threads are not writing
to same file at same time. A performance improvment im considering
is populating a separate collection within the inner loop ( userList.par.map(yUser => {)
and then writing that collection out to seperate file.
Are there other methods I can use to improve performance. So that I can
handle a List that contains 8000 items instead of above example of 100 ?
I'm not sure if you removed too much code for clarity, but from what I can see, there is absolutely nothing that can run in parallel since the only thing you are doing is writing to a file.
EDIT:
One thing that you should do is to move the getDistance(...) computation before the synchronized call to appendToFile, otherwise your parallelized code ends up being sequential.
Instead of calling a synchronized appendToFile, I would call appendToFile in a non-synchronized way, but have each call to that method add the new line to some synchronized queue. Then I would have another thread that flushes that queue to disk periodically. But then you would also need to add something to make sure that the queue is also flushed when all computations are done. So that could get complicated...
Alternatively, you could also keep your code and simply drop the synchronization around the call to appendToFile. It seems that println itself is synchronized. However, that would be risky since println is not officially synchronized and it could change in future versions.

How to sort this lua table?

I have next structure
self.modules = {
["Announcements"] = {
priority = 0,
-- Tons of other attributes
},
["Healthbar"] = {
priority = 40,
-- Tons of other attributes
},
["Powerbar"] = {
priority = 35,
-- Tons of other attributes
},
}
I need to sort this table by priorty DESC, other values does not matter.
E.g. Healthbar first, then Powerbar, and then going all others.
// edit.
Keys must be preserved.
// edit #2
Found a solution, thanks you all.
local function pairsByPriority(t)
local registry = {}
for k, v in pairs(t) do
tinsert(registry, {k, v.priority})
end
tsort(registry, function(a, b) return a[2] > b[2] end)
local i = 0
local iter = function()
i = i + 1
if (registry[i] ~= nil) then
return registry[i][1], t[registry[i][1]]
end
return nil
end
return iter
end
You can't sort a records table because entries are ordered internally by Lua and you can't change the order.
An alternative is to create an array where each entry is a table containing two fields (name and priority) and sort that table instead something like this:
self.modulesArray = {}
for k,v in pairs(self.modules) do
v.name = k --Store the key in an entry called "name"
table.insert(self.modulesArray, v)
end
table.sort(self.modulesArray, function(a,b) return a.priority > b.priority end)
for k,v in ipairs(self.modulesArray) do
print (k,v.name)
end
Output:
1 Healthbar 40
2 Powerbar 35
3 Announcements 0

Resources