1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
|
local expected_dtime = tonumber(minetest.settings:get("dedicated_server_step")) or 0.09
local subscribe_for_modification = mesecons_debug.settings._subscribe_for_modification
local max_penalty = mesecons_debug.settings.max_penalty
subscribe_for_modification("max_penalty", function(value)
max_penalty = value
end)
local moderate_lag_ratio = mesecons_debug.settings.moderate_lag_ratio
subscribe_for_modification("moderate_lag_ratio", function(value)
moderate_lag_ratio = value
end)
local high_lag_ratio = mesecons_debug.settings.high_lag_ratio
local high_lag_dtime = expected_dtime * high_lag_ratio
subscribe_for_modification("high_lag_ratio", function(value)
high_lag_ratio = value
high_lag_dtime = expected_dtime * value
end)
local high_load_threshold = mesecons_debug.settings.high_load_threshold
subscribe_for_modification("high_load_threshold", function(value)
high_load_threshold = value
end)
local penalty_check_steps = mesecons_debug.settings.penalty_check_steps
subscribe_for_modification("penalty_check_steps", function(value)
penalty_check_steps = value
end)
local high_penalty_scale = mesecons_debug.settings.high_penalty_scale
subscribe_for_modification("high_penalty_scale", function(value)
high_penalty_scale = value
end)
local high_penalty_offset = mesecons_debug.settings.high_penalty_offset
subscribe_for_modification("high_penalty_offset", function(value)
high_penalty_offset = value
end)
local medium_penalty_scale = mesecons_debug.settings.medium_penalty_scale
subscribe_for_modification("medium_penalty_scale", function(value)
medium_penalty_scale = value
end)
local medium_penalty_offset = mesecons_debug.settings.medium_penalty_offset
subscribe_for_modification("medium_penalty_offset", function(value)
medium_penalty_offset = value
end)
local low_penalty_scale = mesecons_debug.settings.low_penalty_scale
subscribe_for_modification("low_penalty_scale", function(value)
low_penalty_scale = value
end)
local low_penalty_offset = mesecons_debug.settings.low_penalty_offset
subscribe_for_modification("low_penalty_offset", function(value)
low_penalty_offset = value
end)
local relative_load_max = mesecons_debug.settings.relative_load_clamp
local relative_load_min = 1 / mesecons_debug.settings.relative_load_clamp
subscribe_for_modification("relative_load_clamp", function(value)
relative_load_max = value
relative_load_min = 1 / value
end)
-- see https://en.wikipedia.org/w/index.php?title=Moving_average&oldid=1069105690#Exponential_moving_average
local averaging_coefficient = mesecons_debug.settings.averaging_coefficient
subscribe_for_modification("averaging_coefficient", function(value)
averaging_coefficient = value
end)
local max = math.max
local min = math.min
local function clamp(low, value, high)
return max(low, min(value, high))
end
local function clamp_load(load)
return max(relative_load_min, min(load, relative_load_max))
end
local function update_average(current, history)
return (current * averaging_coefficient) + (history * (1 - averaging_coefficient))
end
local has_monitoring = mesecons_debug.has.monitoring
local mapblock_count, penalized_mapblock_count
if has_monitoring then
mapblock_count = monitoring.gauge("mesecons_debug_mapblock_count", "count of tracked mapblocks")
penalized_mapblock_count =
monitoring.gauge("mesecons_debug_penalized_mapblock_count", "count of penalized mapblocks")
end
local elapsed_steps = 0
local elapsed = 0
minetest.register_globalstep(function(dtime)
elapsed = elapsed + dtime
elapsed_steps = elapsed_steps + 1
--[[
we check every N steps instead of every T seconds because we are more interested in the length of the steps
than in the number of them.
we also force a check if a particular step takes quite a long time, to keep things responsive.
]]
if dtime < high_lag_dtime and elapsed_steps < penalty_check_steps then
return
end
local context_store_size = mesecons_debug.context_store_size -- # of blocks w/ active mesecons
local total_micros = mesecons_debug.total_micros
local total_micros_per_second = total_micros / elapsed
local avg_total_micros_per_second =
update_average(total_micros_per_second, mesecons_debug.avg_total_micros_per_second)
mesecons_debug.avg_total_micros_per_second = avg_total_micros_per_second
-- how much lag is there?
local lag = elapsed / (elapsed_steps * expected_dtime)
local avg_lag = update_average(lag, mesecons_debug.avg_lag)
mesecons_debug.avg_lag = avg_lag
local is_high_lag = avg_lag > high_lag_ratio
local is_moderate_lag = avg_lag > moderate_lag_ratio
-- for use by HUD
if is_high_lag then
mesecons_debug.lag_level = "high"
elseif is_moderate_lag then
mesecons_debug.lag_level = "moderate"
else
mesecons_debug.lag_level = "low"
end
if context_store_size == 0 or avg_total_micros_per_second == 0 then
-- nothing to do, but reset counters
elapsed = 0
elapsed_steps = 0
mesecons_debug.total_micros = 0
mesecons_debug.load_level = "none"
return
end
-- how much of the lag was mesecons?
local mesecons_load = avg_total_micros_per_second / 1000000
local is_high_load = mesecons_load > high_load_threshold
-- for use by HUD
if is_high_load then
mesecons_debug.load_level = "high"
else
mesecons_debug.load_level = "low"
end
local penalty_scale, penalty_offset
if is_high_lag or (is_moderate_lag and is_high_load) then
penalty_scale = high_penalty_scale
penalty_offset = high_penalty_offset
elseif is_moderate_lag then
penalty_scale = medium_penalty_scale
penalty_offset = medium_penalty_offset
else
penalty_scale = low_penalty_scale
penalty_offset = low_penalty_offset
end
-- avg load per active context
local avg_avg_micros_per_second = avg_total_micros_per_second / context_store_size
local penalized_count = 0 -- for monitoring
for _, ctx in pairs(mesecons_debug.context_store) do
if not ctx.whitelisted then
-- moving avg
local micros_per_second = ctx.micros / elapsed
local avg_micros_per_second = update_average(micros_per_second, ctx.avg_micros_per_second)
ctx.avg_micros_per_second = avg_micros_per_second
local relative_load = clamp_load(avg_micros_per_second / avg_avg_micros_per_second)
local new_penalty = ctx.penalty + (relative_load * penalty_scale) + penalty_offset
ctx.penalty = clamp(0, new_penalty, max_penalty)
if has_monitoring and new_penalty > 0 then
penalized_count = penalized_count + 1
end
-- reset cpu usage counter
ctx.micros = 0
end
end
if has_monitoring then
mapblock_count.set(mesecons_debug.context_store_size)
penalized_mapblock_count.set(penalized_count)
end
-- reset counters
elapsed = 0
elapsed_steps = 0
mesecons_debug.total_micros = 0
end)
|