New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Batching APIs #3198
Comments
Why? |
For things such as adding recipes, it's supposed to be faster when adding procedurally generated stuff. For things such as crafting, it's supposed to be faster when crafting multiple recipes at once. (megafurnace that can be smelting 10 different items at a time, all in parallel?) Mainly a LuaJIT optimization. (e.g. procedural generation + recipe registration is a loop, vs procedural generation into a table, then a single Lua/C API call to register all recipes) |
What is your exact use case? I'm not seeing how this would improve the API, but maybe I'm missing something. As example, instead of
you'd write with this batch API
That doesn't really add or remove anything. It neither does remove lines of code, nor does it add something to the readability. Quite the opposite actually regarding your your suggestion regarding multiple arguments.
Would turn into
which makes it harder to find the values that belong to each other. I can see where you want to go if you mean an optimization, removing and reducing function calls can indeed lead to better performance, but this is maybe giving better performance with a very hard downside. Also, Additionally most procedural generation is happening inside loops, so you're swapping function calls against memory/appending to a table to gather first all values and then insert them at one swoop. This might actually be needed at some areas, but the handling of crafts isn't one of them. |
This wouldn't improve performance as craft recipes are registered on server load and it wound decrease readability a lot. |
Heh lol I guess we would trade the speed benefit (if there is any) in thanks to the additional table lookup we do with I don't think this is needed, startup is pretty fast. |
For me, it takes 1.9 seconds to register 20k craft recipes with the test I wrote for #2210. That's 0.5 ms per recipe. Can you implement such a batch call, and benchmark that? |
@RobertZenz @red-001 local list = {1, 5, 10, 25, 50, 100, 200, 500, 1000, 2500, 5000, 10000}
local function registerRecipesRecursive(left, index, recipe, id)
if left > 0 then
if index > 9 then return end -- max 9 items
for x, v in ipairs(list) do
-- check if the item is suitable (avoid duplicated recipes)
if (index == 1 or v >= list[recipe[index - 1]]) and v <= left then
local newRecipe = {}
for k,v in pairs(recipe) do
newRecipe[k] = v
end
newRecipe[index] = x
registerRecipesRecursive(left - v, index + 1, newRecipe, id)
end
end
elseif left == 0 then
local recipeData = {}
for i,v in ipairs(recipe) do
recipeData[i] = "money:money " .. v
end
minetest.register_craft({
type = "shapeless",
output = "money:money " .. id,
recipe = recipeData,
})
end
end
local function registerRecipes()
for i=1,#list do
registerRecipesRecursive(list[i], 1, {}, i)
end
end vs local list = {1, 5, 10, 25, 50, 100, 200, 500, 1000, 2500, 5000, 10000}
local function registerRecipesRecursive(left, index, recipe, id, result)
if left > 0 then
if index > 9 then return end -- max 9 items
for x, v in ipairs(list) do
-- check if the item is suitable (avoid duplicated recipes)
if (index == 1 or v >= list[recipe[index - 1]]) and v <= left then
local newRecipe = {}
for k,v in pairs(recipe) do
newRecipe[k] = v
end
newRecipe[index] = x
registerRecipesRecursive(left - v, index + 1, newRecipe, id, result)
end
end
elseif left == 0 then
local recipeData = {}
for i,v in ipairs(recipe) do
recipeData[i] = "money:money " .. v
end
table.insert(result, {
type = "shapeless",
output = "money:money " .. id,
recipe = recipeData,
})
end
end
local function registerRecipes()
local result = {}
for i=1,#list do
registerRecipesRecursive(list[i], 1, {}, i, result)
end
minetest.batch.register_craft(result)
end Really doesn't change much when it comes to readability. It shouldn't be varargs because you can't table.insert into varargs (altho varargs would potentially be faster), and you'd have issues with multiple arguments if one (or more) of the arguments were optional. |
Some people say table.insert is slow? |
@est31 Not in LuaJIT. In LuaJIT table.insert is actually faster than keeping track of the count/size yourself. |
@SoniEx2 It's a little bit late on my end, so excuse me if I'm wrong, but am I seeing this right that this function registers 12 "coin sizes" and (most?) possible receipts for that? |
@RobertZenz All possible 3x3 recipes (e.g. it won't try to do a 10000 x "coin:coin 1" recipe), yes. |
I know that this is off-topic, but why not simply let the coins stack until 10,000? You only have one coin, no different sizes, no special cases, just damn big stacks. |
I doubt that this will result in a useful performance increase. |
If not directly result in a performance increase, it will, at least, encourage batching. Alternatively add a "modding best practices" wiki page and hope people read it... |
@SoniEx2 what would be the benefit of this? It seems you only want to encourage a different style. |
The benefits are to:
... I guess a "modding best practices" wiki page would be a better idea... (should include things such as "avoid loadstring, pass functions around instead") |
To test whether batching in fact increases speed, as you claim, I've implemented what you request, together with a benchmark: https://github.com/est31/minetest/commits/batch_api_test To run the test, just get the code, and start a world with the minimal game. It will test-wise register 20k recipes, and print the benchmark results to stdout. Using LuaJIT, it gives me output:
Using bundled lua, it gives me:
You are right, there is an advantage of using the batched method, but the advantage is so small (2% to 4%), that I don't think we should add it. Therefore, closing. |
What if you use procedural generation (e.g. the code I provided)? |
I don't think it would be much different. If you want, you can try it, but I doubt your results will be different from mine. |
Also was table.insert in plain Lua actually faster than the traditional method?! I wasn't expecting that one... O_o |
Things such as minetest.register_craft could be batched (e.g. minetest.batch.register_craft()).
Instead of taking a single recipe, it'd take a table of recipes. e.g.
For multi-argument functions, it'd take a table for each argument.
For return values, it'd return a table for each return. e.g.
The text was updated successfully, but these errors were encountered: