123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356 |
- -- Minetest: builtin/game/mod_profiling.lua
- local mod_statistics = {}
- mod_statistics.step_total = 0
- mod_statistics.data = {}
- mod_statistics.stats = {}
- mod_statistics.stats["total"] = {
- min_us = math.huge,
- max_us = 0,
- avg_us = 0,
- min_per = 100,
- max_per = 100,
- avg_per = 100
- }
- local replacement_table = {
- "register_globalstep",
- "register_on_placenode",
- "register_on_dignode",
- "register_on_punchnode",
- "register_on_generated",
- "register_on_newplayer",
- "register_on_dieplayer",
- "register_on_respawnplayer",
- "register_on_prejoinplayer",
- "register_on_joinplayer",
- "register_on_leaveplayer",
- "register_on_cheat",
- "register_on_chat_message",
- "register_on_player_receive_fields",
- "register_on_mapgen_init",
- "register_on_craft",
- "register_craft_predict",
- "register_on_item_eat"
- }
- --------------------------------------------------------------------------------
- function mod_statistics.log_time(type, modname, time_in_us)
- if modname == nil then
- modname = "builtin"
- end
-
- if mod_statistics.data[modname] == nil then
- mod_statistics.data[modname] = {}
- end
-
- if mod_statistics.data[modname][type] == nil then
- mod_statistics.data[modname][type] = 0
- end
-
- mod_statistics.data[modname][type] =
- mod_statistics.data[modname][type] + time_in_us
- mod_statistics.step_total = mod_statistics.step_total + time_in_us
- end
- --------------------------------------------------------------------------------
- function mod_statistics.update_statistics(dtime)
- for modname,types in pairs(mod_statistics.data) do
-
- if mod_statistics.stats[modname] == nil then
- mod_statistics.stats[modname] = {
- min_us = math.huge,
- max_us = 0,
- avg_us = 0,
- min_per = 100,
- max_per = 0,
- avg_per = 0
- }
- end
-
- local modtime = 0
- for type,time in pairs(types) do
- modtime = modtime + time
- if mod_statistics.stats[modname].types == nil then
- mod_statistics.stats[modname].types = {}
- end
- if mod_statistics.stats[modname].types[type] == nil then
- mod_statistics.stats[modname].types[type] = {
- min_us = math.huge,
- max_us = 0,
- avg_us = 0,
- min_per = 100,
- max_per = 0,
- avg_per = 0
- }
- end
-
- local toupdate = mod_statistics.stats[modname].types[type]
-
- --update us values
- toupdate.min_us = math.min(time, toupdate.min_us)
- toupdate.max_us = math.max(time, toupdate.max_us)
- --TODO find better algorithm
- toupdate.avg_us = toupdate.avg_us * 0.99 + modtime * 0.01
-
- --update percentage values
- local cur_per = (time/mod_statistics.step_total) * 100
- toupdate.min_per = math.min(toupdate.min_per, cur_per)
-
- toupdate.max_per = math.max(toupdate.max_per, cur_per)
-
- --TODO find better algorithm
- toupdate.avg_per = toupdate.avg_per * 0.99 + cur_per * 0.01
-
- mod_statistics.data[modname][type] = 0
- end
-
- --per mod statistics
- --update us values
- mod_statistics.stats[modname].min_us =
- math.min(modtime, mod_statistics.stats[modname].min_us)
- mod_statistics.stats[modname].max_us =
- math.max(modtime, mod_statistics.stats[modname].max_us)
- --TODO find better algorithm
- mod_statistics.stats[modname].avg_us =
- mod_statistics.stats[modname].avg_us * 0.99 + modtime * 0.01
-
- --update percentage values
- local cur_per = (modtime/mod_statistics.step_total) * 100
- mod_statistics.stats[modname].min_per =
- math.min(mod_statistics.stats[modname].min_per, cur_per)
-
- mod_statistics.stats[modname].max_per =
- math.max(mod_statistics.stats[modname].max_per, cur_per)
-
- --TODO find better algorithm
- mod_statistics.stats[modname].avg_per =
- mod_statistics.stats[modname].avg_per * 0.99 + cur_per * 0.01
- end
-
- --update "total"
- mod_statistics.stats["total"].min_us =
- math.min(mod_statistics.step_total, mod_statistics.stats["total"].min_us)
- mod_statistics.stats["total"].max_us =
- math.max(mod_statistics.step_total, mod_statistics.stats["total"].max_us)
- --TODO find better algorithm
- mod_statistics.stats["total"].avg_us =
- mod_statistics.stats["total"].avg_us * 0.99 +
- mod_statistics.step_total * 0.01
-
- mod_statistics.step_total = 0
- end
- --------------------------------------------------------------------------------
- local function build_callback(log_id, fct)
- return function( toregister )
- local modname = core.get_current_modname()
-
- fct(function(...)
- local starttime = core.get_us_time()
- -- note maximum 10 return values are supported unless someone finds
- -- a way to store a variable lenght return value list
- local r0, r1, r2, r3, r4, r5, r6, r7, r8, r9 = toregister(...)
- local delta = core.get_us_time() - starttime
- mod_statistics.log_time(log_id, modname, delta)
- return r0, r1, r2, r3, r4, r5, r6, r7, r8, r9
- end
- )
- end
- end
- --------------------------------------------------------------------------------
- function profiling_print_log(cmd, filter)
- print("Filter:" .. dump(filter))
- core.log("action", "Values below show times/percentages per server step.")
- core.log("action", "Following suffixes are used for entities:")
- core.log("action", "\t#oa := on_activate, #os := on_step, #op := on_punch, #or := on_rightclick, #gs := get_staticdata")
- core.log("action",
- string.format("%16s | %25s | %10s | %10s | %10s | %9s | %9s | %9s",
- "modname", "type" , "min µs", "max µs", "avg µs", "min %", "max %", "avg %")
- )
- core.log("action",
- "-----------------+---------------------------+-----------+" ..
- "-----------+-----------+-----------+-----------+-----------")
- for modname,statistics in pairs(mod_statistics.stats) do
- if modname ~= "total" then
-
- if (filter == "") or (modname == filter) then
- if modname:len() > 16 then
- modname = "..." .. modname:sub(-13)
- end
-
- core.log("action",
- string.format("%16s | %25s | %9d | %9d | %9d | %9d | %9d | %9d",
- modname,
- " ",
- statistics.min_us,
- statistics.max_us,
- statistics.avg_us,
- statistics.min_per,
- statistics.max_per,
- statistics.avg_per)
- )
- if core.setting_getbool("detailed_profiling") then
- if statistics.types ~= nil then
- for type,typestats in pairs(statistics.types) do
-
- if type:len() > 25 then
- type = "..." .. type:sub(-22)
- end
-
- core.log("action",
- string.format(
- "%16s | %25s | %9d | %9d | %9d | %9d | %9d | %9d",
- " ",
- type,
- typestats.min_us,
- typestats.max_us,
- typestats.avg_us,
- typestats.min_per,
- typestats.max_per,
- typestats.avg_per)
- )
- end
- end
- end
- end
- end
- end
- core.log("action",
- "-----------------+---------------------------+-----------+" ..
- "-----------+-----------+-----------+-----------+-----------")
-
- if filter == "" then
- core.log("action",
- string.format("%16s | %25s | %9d | %9d | %9d | %9d | %9d | %9d",
- "total",
- " ",
- mod_statistics.stats["total"].min_us,
- mod_statistics.stats["total"].max_us,
- mod_statistics.stats["total"].avg_us,
- mod_statistics.stats["total"].min_per,
- mod_statistics.stats["total"].max_per,
- mod_statistics.stats["total"].avg_per)
- )
- end
- core.log("action", " ")
-
- return true
- end
- --------------------------------------------------------------------------------
- local function initialize_profiling()
- core.log("action", "Initialize tracing")
-
- mod_statistics.entity_callbacks = {}
-
- -- first register our own globalstep handler
- core.register_globalstep(mod_statistics.update_statistics)
-
- local rp_register_entity = core.register_entity
- core.register_entity = function(name, prototype)
- local modname = core.get_current_modname()
- local new_on_activate = nil
- local new_on_step = nil
- local new_on_punch = nil
- local new_rightclick = nil
- local new_get_staticdata = nil
-
- if prototype.on_activate ~= nil then
- local cbid = name .. "#oa"
- mod_statistics.entity_callbacks[cbid] = prototype.on_activate
- new_on_activate = function(self, staticdata, dtime_s)
- local starttime = core.get_us_time()
- mod_statistics.entity_callbacks[cbid](self, staticdata, dtime_s)
- local delta = core.get_us_time() -starttime
- mod_statistics.log_time(cbid, modname, delta)
- end
- end
-
- if prototype.on_step ~= nil then
- local cbid = name .. "#os"
- mod_statistics.entity_callbacks[cbid] = prototype.on_step
- new_on_step = function(self, dtime)
- local starttime = core.get_us_time()
- mod_statistics.entity_callbacks[cbid](self, dtime)
- local delta = core.get_us_time() -starttime
- mod_statistics.log_time(cbid, modname, delta)
- end
- end
-
- if prototype.on_punch ~= nil then
- local cbid = name .. "#op"
- mod_statistics.entity_callbacks[cbid] = prototype.on_punch
- new_on_punch = function(self, hitter)
- local starttime = core.get_us_time()
- mod_statistics.entity_callbacks[cbid](self, hitter)
- local delta = core.get_us_time() -starttime
- mod_statistics.log_time(cbid, modname, delta)
- end
- end
-
- if prototype.rightclick ~= nil then
- local cbid = name .. "#rc"
- mod_statistics.entity_callbacks[cbid] = prototype.rightclick
- new_rightclick = function(self, clicker)
- local starttime = core.get_us_time()
- mod_statistics.entity_callbacks[cbid](self, clicker)
- local delta = core.get_us_time() -starttime
- mod_statistics.log_time(cbid, modname, delta)
- end
- end
-
- if prototype.get_staticdata ~= nil then
- local cbid = name .. "#gs"
- mod_statistics.entity_callbacks[cbid] = prototype.get_staticdata
- new_get_staticdata = function(self)
- local starttime = core.get_us_time()
- local retval = mod_statistics.entity_callbacks[cbid](self)
- local delta = core.get_us_time() -starttime
- mod_statistics.log_time(cbid, modname, delta)
- return retval
- end
- end
-
- prototype.on_activate = new_on_activate
- prototype.on_step = new_on_step
- prototype.on_punch = new_on_punch
- prototype.rightclick = new_rightclick
- prototype.get_staticdata = new_get_staticdata
-
- rp_register_entity(name,prototype)
- end
-
- for i,v in ipairs(replacement_table) do
- local to_replace = core[v]
- core[v] = build_callback(v, to_replace)
- end
-
- local rp_register_abm = core.register_abm
- core.register_abm = function(spec)
-
- local modname = core.get_current_modname()
-
- local replacement_spec = {
- nodenames = spec.nodenames,
- neighbors = spec.neighbors,
- interval = spec.interval,
- chance = spec.chance,
- action = function(pos, node, active_object_count, active_object_count_wider)
- local starttime = core.get_us_time()
- spec.action(pos, node, active_object_count, active_object_count_wider)
- local delta = core.get_us_time() - starttime
- mod_statistics.log_time("abm", modname, delta)
- end
- }
- rp_register_abm(replacement_spec)
- end
-
- core.log("action", "Mod profiling initialized")
- end
- initialize_profiling()
|