add jit parameter tuning and get_node_raw access

This commit is contained in:
kno10 2025-01-16 17:55:39 +01:00 committed by the-real-herowl
parent 36f5abc4df
commit 9aa35154d2
8 changed files with 184 additions and 1 deletions

33
mods/CORE/mcl_init/API.md Normal file
View file

@ -0,0 +1,33 @@
# mcl_init
Initialization of VoxeLibre, in particular some shared variables exposed via `mcl_vars`.
## Optimized LuaJIT parameters
As of 2024, the default LuaJIT parameters are not well tuned, *depending on the version you use*.
According to <https://luajit.org/running.html>, standard LuaJIT uses:
| maxtrace | 1000 | Max. number of traces in the cache |
| maxrecord | 4000 | Max. number of recorded IR instructions |
| maxirconst | 500 | Max. number of IR constants of a trace |
| minstitch | 0 | Min. # of IR ins for a stitched trace. |
| maxmcode | 512 | Max. total size of all machine code areas in KBytes |
However, the openresty branch uses more sane defaults for a long time <https://github.com/openresty/luajit2/blob/v2.1-agentzh/src/lj_jit.h#L117>:
| maxtrace | 8000 | Max. number of traces in the cache |
| maxrecord | 16000 | Max. number of recorded IR instructions |
| maxirconst | 500 | Max. number of IR constants of a trace |
| minstitch | 3 | Min. # of IR ins for a stitched trace. |
| maxmcode | 40960 | Max. total size of all machine code areas in KBytes |
Mineclonia contributor "halon" investigated this, and
increasing these values appears to be beneficial for improving the performance of Luanti,
although users of openresty (e.g., on Debian GNU/Linux) may not notice a difference.
TODO: every few years, the situation should be re-assessed. For example, Luanti upstream might set improved values.
Unfortunately, it does not appear that we can query the values, only set them.

View file

@ -0,0 +1,66 @@
-- NOTE: As of Luanti 5.9 and 5.10, the function `core.get_node_raw` is NOT exposed.
-- However, via `secure.trusted_mods=vl_trusted` it is possible to expose and use it.
-- If <https://github.com/minetest/minetest/issues/15317> is merged, it may become public API.
-- For more details, see the vl_trusted module.
-- We do not call into vl_trusted directly, but it set `core.get_node_raw` if loaded first.
-- Load order hence is important, and this mod should depend on vl_trusted.
local core_get_node = core.get_node
local core_get_node_raw = core.get_node_raw
local core_get_name_from_content_id = core.get_name_from_content_id
local core_get_content_id = core.get_content_id
--- Get the node name, param and param2 using `core.get_node_raw` if available, fall back to regular get_node otherwise.
---
--- @param pos vector: position
--- @return (string, number, number): node name, param1 and param2
function mcl_vars.get_node_name(pos) -- Fallback version
local node = core_get_node(pos)
return node.name, node.param1, node.param2
end
-- optimized version
if core_get_node_raw then
mcl_vars.get_node_name = function(pos)
local content, param1, param2, pos_ok = core_get_node_raw(pos.x, pos.y, pos.z)
if not pos_ok then return "ignore", 0, 0 end
return core_get_name_from_content_id(content), param1, param2
end
end
--- Get the node name, param and param2 using `core.get_node_raw` if available fall back to regular get_node otherwise.
--- Note: up to Luanti 5.10 at least, this will create a new vector. If you already have a vector, prefer `get_node_name`.
---
--- @param x number: coordinate
--- @param y number: coordinate
--- @param z number: coordinate
--- @return (string, number, number): node name, param1, param2
function mcl_vars.get_node_name_raw(x, y, z) -- Fallback version
local node = core_get_node({x=x,y=y,z=z}) -- raw table, not need for vector
return node.name, node.param1, node.param2
end
-- optimized version
if core_get_node_raw then
mcl_vars.get_node_name_raw = function(x, y, z)
local content, param1, param2, pos_ok = core_get_node_raw(x, y, z)
if not pos_ok then return "ignore", 0, 0 end
return core_get_name_from_content_id(content), param1, param2
end
end
--- Get the node name, param and param2 using `core.get_node_raw` if available, fall back to regular get_node otherwise.
--- Note: up to Luanti 5.10 at least, this involves an unnecessary roundtrip via the node name.
--- If you use the node name anyway, prefer `get_node_name_raw` or `get_node_name`.
---
--- @param x number: coordinate
--- @param y number: coordinate
--- @param z number: coordinate
--- @return (number, number, number, boolean): node content id, param1, param2, pos_ok
function mcl_vars.get_node_raw(x, y, z) -- Fallback
local node = core_get_node({x=x,y=y,z=z}) -- raw table, not need for vector
return core_get_content_id(node.name), node.param1, node.param2, node.name ~= "ignore"
end
-- optimized version
if core_get_node_raw then
mcl_vars.get_node_raw = core_get_node_raw
end

View file

@ -1,5 +1,6 @@
-- Some global variables (don't overwrite them!)
mcl_vars = {}
local modpath = core.get_modpath(core.get_current_modname())
minetest.log("action", "World seed = " .. minetest.get_mapgen_setting("seed"))
@ -22,7 +23,7 @@ if not map_version then
core.set_mapgen_setting("vl_world_version", map_version, true)
end
mcl_vars.map_version = map_version -- make available
core.log("action", "Voxelibre mapgen version = "..map_version)
core.log("action", "VoxeLibre mapgen version = "..map_version)
mcl_vars.redstone_tick = 0.1
@ -216,6 +217,7 @@ minetest.craftitemdef_default.stack_max = 64
-- Set random seed for all other mods (Remember to make sure no other mod calls this function)
math.randomseed(os.time())
---DEPRECATED. If you need to ensure the area is emerged, use LVM.
---"Trivial" (actually NOT) function to just read the node and some stuff to not just return "ignore", like mt 5.4 does.
---@param pos Vector Position, if it's wrong, `{name="error"}` node will return.
---@param force? boolean Optional (default: `false`), Do the maximum to still read the node within us_timeout.
@ -252,3 +254,6 @@ function mcl_vars.get_node(pos, force, us_timeout)
-- it still can return "ignore", LOL, even if force = true, but only after time out
end
dofile(modpath.."/tune_jit.lua")
dofile(modpath.."/get_node_name.lua")

View file

@ -1,3 +1,4 @@
name = mcl_init
author = Wuzzy
description = Initialization mod of VoxeLibre. Defines some common shared variables and sets up initial default settings which have to be set at the beginning.
optional_depends = vl_trusted

View file

@ -0,0 +1,23 @@
--- This code is largely based on the work by halon for mineclonia, but encapsulated differently
local luajit_present = core.global_exists("jit")
-- Increased limits for the JIT, as of 2024
-- TODO: re-assess this every year or so, as either upstream Luanti may
-- eventually increase these limits itself, or the luajit libraries increase
-- their parameters - e.g., the openresty version already has increase limits
-- because apparently we can not query the JIT parameters
if luajit_present then
local status, opt = jit.status()
if not status then
core.log("warning", "[mcl_init] LuaJIT appears to be available, but turned off. This will result in degraded performance.")
end
jit.opt.start(
"maxtrace=24000",
"maxrecord=32000",
"minstitch=3",
"maxmcode=163840"
)
core.log("action", "[mcl_init] increased LuaJIT parameters. LuaJIT version: "..jit.version.." with flags "..tostring(opt))
else
core.log("warning", "[mcl_init] LuaJIT not detected - it is strongly recommended to build luanti with LuaJIT for performance reasons!")
end

View file

@ -0,0 +1,22 @@
# vl_trusted
This module does not provide a public API.
The optimized function calls are only available via existing APIs.
We currently implement one feature that require trusted access:
## Access to `core.get_node_raw`
The `core.get_node_raw` function has been added to Luanti in version 5.9.0, but as of 5.10 is not a public API,
although we have asked for this to be made public: <https://github.com/minetest/minetest/issues/15317>
This function is beneficial as it does not create a table for the return, which reduces the amount of garbage collection necessary,
in particular as LuaJIT's allocation sinking does not appear to eliminate these, unfortunately.
For compatibility, we expose this with slightly different semantics, which are a tradeoff between using the new API when available
(or exposed via this trusted module), and having to be able to fall back to the regular `core.get_node` call.
TODO: when the minimum version of Luanti has a public version of the API, these wrappers should likely be removed and the unmodified
`core.get_node_raw` should be used where possible.

View file

@ -0,0 +1,30 @@
--- Make `core.get_node_raw` public. It's safe to use and stable.
--
-- This code is based on the work by halon for mineclonia, but encapsulated differently
-- Check if `core.get_node_raw` is public by now.
if not core.get_node_raw then
-- try to un-hide the function
local ie = core.request_insecure_environment()
if not ie then
core.log("action", "[vl_trusted] cannot unhide get_node_raw, please add vl_trusted to secure.trusted_mods to improve performance (optional).")
elseif not ie.debug or not ie.debug.getupvalue then
core.log("warning", "[vl_trusted] debug.getupvalue is not available, unhiding does not work. Version: "..dump(core.get_version(),""))
else
for i=1,5 do -- will not be five levels deep
local name, upvalue = ie.debug.getupvalue(core.get_node, i)
if not name then break end
if name == "get_node_raw" then
core.get_node_raw = upvalue
break
end
end
if core.get_node_raw then
core.log("action", "[vl_trusted] get_node_raw unhiding successful.")
else
core.log("warning", "[vl_trusted] get_node_raw unhiding NOT successful. Version: "..dump(core.get_version(),""))
end
end
else
core.log("verbose", "[vl_trusted] get_node_raw available without workaround.")
end

View file

@ -0,0 +1,3 @@
name = vl_trusted
author = kno10
description = Optional - access to some protected Luanti functionality for increased performance