Assert caveats
Assert caveats
A few days ago I did some experiments about the use of assert and was going to write about the results here. Then I thought it was worth a blog post, so I asked on IRC. Since it's not really LÖVE-specific, I was advised to post it on lua.space. So I did.
Here's the post, if someone is interested:
http://lua.space/general/assert-usage-caveat
Thanks to undef for the help polishing and debugging it.
Here's the post, if someone is interested:
http://lua.space/general/assert-usage-caveat
Thanks to undef for the help polishing and debugging it.
- slime
- Solid Snayke
- Posts: 3172
- Joined: Mon Aug 23, 2010 6:45 am
- Location: Nova Scotia, Canada
- Contact:
Re: Assert caveats
LuaJIT 2.1 can JIT-compile string concatenation operations.
Re: Assert caveats
Nice. It would be interesting to run the benchmark with it.
Re: Assert caveats
There is a subtlety with the line:
This creates an intermediate table in memory.
So when you run the code it's quite fast but
LATER when then the garbage collector kicks in
you may get a slight delay.
In short, the benchmark should take memory usage into account as well.
Also, the following is an unfair test:
unless the function is moved outside of any loops:
PS. Table concat is good when you have a lot of arguments.
Something with recursion may work better for small number of args:
or string format could work pretty well:
Code: Select all
local args = {...}
So when you run the code it's quite fast but
LATER when then the garbage collector kicks in
you may get a slight delay.
In short, the benchmark should take memory usage into account as well.
Also, the following is an unfair test:
Code: Select all
xassert(true, function() return "blah" .. i end)
Code: Select all
local _func = function(i) return "blah" .. i end
for i = 1, ntests do xassert(true, _func, i) end
Something with recursion may work better for small number of args:
Code: Select all
local function _concat(a, ...)
if a == nil then return '' end
return a .. _concat(...)
end
function rassert(condition, ...)
if condition then return true end
error(_concat(...))
end
Code: Select all
function fassert(condition, f, ...)
if condition then return true end
error(string.format(f, ...))
end
Re: Assert caveats
Interesting article. You could get rid of the intermediary table, table.remove and unpack by using a named parameter for the callback; I guess this would probably improve performance a bit.
Code: Select all
function xassert(a, f, ...)
if a then return a, f, ... end
if type(f) == "function" then
error(f(...), 2)
else
error(f or "assertion failed!", 2)
end
end
Re: Assert caveats
That only happens when the assertion fails, which typically makes the program stop and therefore at that point it doesn't matter.ivan wrote:There is a subtlety with the line:This creates an intermediate table in memory.Code: Select all
local args = {...}
That's the reason for the note at the very end. In some use cases, an assertion failure might not make the program stop, and as the note says, speed might be affected in such cases, for the reason you mention.
My first idea to solve the problem was to make inline functions that would construct the string, which would not be called unless the assertion failed. In your version, what is being benchmarked is basically the same as in the string.concat version, namely parameter passing.ivan wrote:Also, the following is an unfair test:unless the function is moved outside of any loops:Code: Select all
xassert(true, function() return "blah" .. i end)
Code: Select all
local _func = function(i) return "blah" .. i end for i = 1, ntests do xassert(true, _func, i) end
It's only for the failed assertion case, so speed and memory usage don't really need to be considered. That's why I went with a short and readable version.ivan wrote:PS. Table concat is good when you have a lot of arguments.
Something with recursion may work better for small number of args:Code: Select all
local function _concat(a, ...) if a == nil then return '' end return a .. _concat(...) end
Last edited by pgimeno on Wed Feb 10, 2016 6:53 am, edited 1 time in total.
Re: Assert caveats
Thanks. There's a subtlety with that approach: with one arg, it returns two values instead of one, making it incompatible. With assert, you can do e.g. io.write(assert("x")) but if you try to do io.write(xassert("x")) using your definition, it will fail with "bad argument #2 to 'write' (string expected, got nil)".airstruck wrote:Interesting article. You could get rid of the intermediary table, table.remove and unpack by using a named parameter for the callback; I guess this would probably improve performance a bit.
Edit: it would be nice to be able to do something like: select("2-999", ...) or simply select("2-", ...)
Re: Assert caveats
Ahh, I see what you mean. I guess you could do this instead:
Here's another idea if you just want built-in concatenation with no separator and no callback:
Not really relevant here, but maybe useful.
Code: Select all
function xassert(a, ...)
if a then return a, ... end
local f = ...
if type(f) == "function" then
error(f(select(2, ...)), 2)
else
error(f or "assertion failed!", 2)
end
end
Code: Select all
function xassert (a, ...)
if a then return a, ... end
local argc = select('#', ...)
if argc > 0 then
error(('%s'):rep(argc):format(...), 2)
else
error('assertion failed!', 2)
end
end
Select already does what you want in this case; it selects the arg at that index and everything after it. This is a pain when you only want to select a fixed range of arguments, but you can use something like this:Edit: it would be nice to be able to do something like: select("2-999", ...) or simply select("2-", ...)
Code: Select all
function pluck (n, ...)
if n < 1 then return end
n = math.min(n, select('#', ...))
return (...), pluck(n - 1, select(2, ...))
end
pluck(5, select(3, ...)) -- selects five args, starting at arg 3
Last edited by airstruck on Wed Feb 10, 2016 8:24 am, edited 1 time in total.
Re: Assert caveats
I see. In that event everything after:pgimeno wrote:That only happens when the assertion fails, which typically makes the program stop and therefore at that point it doesn't matter.
Code: Select all
function xassert(a, ...)
if a then return a, ... end
Yep, the point is when a function is defined within a loop it has its own scope (and unique reference) so it's not a fair benchmark IMO.My first idea to solve the problem was to make inline functions that would construct the string, which would not be called unless the assertion failed. In your version, what is being benchmarked is basically the same as in the string.concat version, namely parameter passing.
Re: Assert caveats
Thanks again for your feedback, guys, I appreciate it.
Oh! That would have indeed simplified the function. Thanks for the information.airstruck wrote:Select already does what you want in this case; it selects the arg at that index and everything after it.Edit: it would be nice to be able to do something like: select("2-999", ...) or simply select("2-", ...)
Who is online
Users browsing this forum: Bing [Bot] and 6 guests