Optimizations
From GiderosMobile
When you're ready to optimize your game, this page can be helpful.
Luau
You can take advantage of Luau new features added to Gideros:
- Integer Divide Operator
- Larger and Smaller Operators
- Mutation Operators
- Native Code Generation
- Trigonometry Conversion Operators
- Type Checking
Various Functions
Gideros 2025.2 introduces setsafeenv(flags) which tells Luau the global environment is "safe" and so enables optimizations. flags parameter indicates which optimizations to enable, and you'll probably want them all by using -1 value.
setsafeenv(-1)
You can add setsafeenv(-1) at the beginning of the following tests to see the difference.
Distance
-- distance (based on simple functions test timer by @antix)
local distance, random, sqrt = math.distance, math.random, math.sqrt
local x1, y1, x2, y2 = random(-10000, 10000), random(-10000, 10000), random(-10000, 10000), random(-10000, 10000)
-- classic (boring!)
local dx, dy
-- with vectors
local p1, p2 = vector(x1, y1), vector(x2, y2)
-- init
local dist = distance(x1, y1, x2, y2)
print("")
print("*** POINT A: "..x1, y1, "POINT B: "..x2, y2)
print("*** DISTANCE: "..dist)
dist = #(p2-p1)
print("*** VECTORS DIST.: "..dist)
print("")
-- tests
local data = {
{
"sqrt ",
function()
dx, dy = x2-x1, y2-y1
dist = sqrt(dx*dx + dy*dy)
end, -- 50ms
},
{
"exponent ",
function()
dx, dy = x2-x1, y2-y1
dist = (dx^2 + dy^2)^0.5
end, -- 37ms
},
{
"multiply/exponent ",
function()
dx, dy = x2-x1, y2-y1
dist = (dx*dx + dy*dy)^0.5
end, -- 40ms
},
{
"multiply/exponent without power",
function()
dx, dy = x2-x1, y2-y1
-- dist = dx*dx + dy*dy
dist = dx^2 + dy^2
end, -- 36ms but wrong result!
},
{
"math.distance ",
function()
dist = distance(x1, y1, x2, y2)
end, -- 53ms
},
{
"Luau VECTORS ",
function()
dist = #(p2-p1)
end, -- 24ms => this is our WINNER (Gideros 2024.11+)
},
}
-- run all functions
for i = 1, #data do
local block = data[i]
local func = block[2]
local start = os.timer()
for i = 1, 1000000 do -- 1 million repetitions!
func()
end
local elapsed = math.floor((os.timer() - start) * 1000)
print(block[1].." ("..elapsed.."ms)", "", "distance: "..dist)
end
--[[ RESULTS
*** POINT A: -4709 7129 POINT B: 5172 7263
*** DISTANCE: 9881.908570716489
*** VECTORS DIST.: 9881.908520118976
sqrt (50ms) distance: 9881.908570716489
exponent (37ms) distance: 9881.908570716489
multiply/exponent (40ms) distance: 9881.908570716489
multiply/exponent without power (36ms) distance: 97652117
math.distance (53ms) distance: 9881.908570716489
Luau VECTORS (24ms) distance: 9881.908520118976]]
Clamp
-- clamp (based on simple functions test timer by @antix)
local clamp, random = math.clamp, math.random
local v, mi, ma = random(-15, 15), -10, 10
-- init
local result = clamp(v, mi, ma)
print("")
print("*** V: "..v, "MIN: "..mi, "MAX: "..ma)
print("*** CLAMPED: "..result)
print("")
local function fclamp(v, min, max)
return (v <> min) >< max
end
-- tests
local data = {
{
"math.clamp ",
function()
result = clamp(v ,mi, ma)
end, -- 38ms
},
{
"clamp function ",
function()
result = fclamp(v ,mi, ma)
end, -- 20ms, no comments!
},
}
-- run all functions
for i = 1, #data do
local block = data[i]
local func = block[2]
local start = os.timer()
for i = 1, 1000000 do -- 1 million repetitions!
func()
end
local elapsed = math.floor((os.timer() - start) * 1000)
print(block[1].." ("..elapsed.."ms)", "", "clamped value: "..result)
end
--[[ RESULTS
*** V: -1 MIN: -10 MAX: 10
*** CLAMPED: -1
math.clamp (38ms) clamped value: -1
clamp function (20ms) clamped value: -1
]]
Sprite Sorting
-- sprite sorting (based on simple functions test timer by @antix)
local random = math.random
local sprites = {}
for i = 1, 64 do
sprites[i] = {}
sprites[i].spr1 = Pixel.new(0x0, 1, 64, 64)
sprites[i].spr1:setPosition(random(1, 640), 48) -- 48, 96
stage:addChild(sprites[i].spr1)
sprites[i].spr2 = Pixel.new(0xff0000, 1, 64, 64)
sprites[i].spr2:setPosition(random(1, 640), 64)
stage:addChild(sprites[i].spr2)
end
local spr1y, spr2y = 0, 0
-- tests
local data = {
{
"swap",
function()
for _, v in pairs(sprites) do
if v.spr1:getY() < v.spr2:getY() then
spr1y, spr2y = v.spr1:getY(), v.spr2:getY()
stage:swapChildren(v.spr1, v.spr2)
end
end
end, -- 816ms (much better than addChildAt)
},
{
"addChildAt",
function()
for _, v in pairs(sprites) do
if v.spr1:getY() < v.spr2:getY() then
spr1y, spr2y = v.spr1:getY(), v.spr2:getY()
stage:addChildAt(v.spr1, stage:getChildIndex(v.spr2))
end
end
end, -- 1272ms
},
{
"async swap",
function()
local function fun()
for _, v in pairs(sprites) do
if v.spr1:getY() < v.spr2:getY() then
spr1y, spr2y = v.spr1:getY(), v.spr2:getY()
stage:swapChildren(v.spr1, v.spr2)
end
end
end
Core.asyncCall(fun) -- profiler seems to be faster without asyncCall (because of pairs traversing?)
end, -- 29ms (the best but has some glitches!)
},
}
-- run all functions
for i = 1, #data do
local block = data[i]
local func = block[2]
local start = os.timer()
for i = 1, 25000 do func() end -- 12500
local elapsed = math.floor((os.timer() - start) * 1000)
print(block[1].." ("..elapsed.."ms)", spr1y, spr2y)
end
More to come, God's willing!