-
Notifications
You must be signed in to change notification settings - Fork 43
Description
Root: next to https://www.tarantool.io/en/doc/latest/book/app_server/luajit_memprof/
We finally added C and Lua API for LuaJIT metrics (#5187).
API
The additional header <lmisclib.h> is introduced to extend the existing LuaJIT
C API with new interfaces. The first function provided via this header is the
following:
/* API for obtaining various platform metrics. */
LUAMISC_API void luaM_metrics(lua_State *L, struct luam_Metrics *metrics);
This function fills the structure pointed to by metrics
with the corresponding
metrics related to Lua state anchored to the given coroutine L
.
The struct luam_Metrics
has the following definition:
struct luam_Metrics {
/*
** Number of strings being interned (i.e. the string with the
** same payload is found, so a new one is not created/allocated).
*/
size_t strhash_hit;
/* Total number of strings allocations during the platform lifetime. */
size_t strhash_miss;
/* Amount of allocated string objects. */
size_t gc_strnum;
/* Amount of allocated table objects. */
size_t gc_tabnum;
/* Amount of allocated udata objects. */
size_t gc_udatanum;
/* Amount of allocated cdata objects. */
size_t gc_cdatanum;
/* Memory currently allocated. */
size_t gc_total;
/* Total amount of freed memory. */
size_t gc_freed;
/* Total amount of allocated memory. */
size_t gc_allocated;
/* Count of incremental GC steps per state. */
size_t gc_steps_pause;
size_t gc_steps_propagate;
size_t gc_steps_atomic;
size_t gc_steps_sweepstring;
size_t gc_steps_sweep;
size_t gc_steps_finalize;
/*
** Overall number of snap restores (amount of guard assertions
** leading to stopping trace executions).
*/
size_t jit_snap_restore;
/* Overall number of abort traces. */
size_t jit_trace_abort;
/* Total size of all allocated machine code areas. */
size_t jit_mcode_size;
/* Amount of JIT traces. */
unsigned int jit_trace_num;
};
All metrics are collected throughout the platform uptime. These metrics
increase monotonically and can overflow:
strhash_hit
strhash_miss
gc_freed
gc_allocated
gc_steps_pause
gc_steps_propagate
gc_steps_atomic
gc_steps_sweepstring
gc_steps_sweep
gc_steps_finalize
jit_snap_restore
jit_trace_abort
They make sense only with comparing with their value from a previous
luaM_metrics()
call.
There is also a complement introduced for Lua space -- misc.getmetrics()
.
This function is just a wrapper for luaM_metrics()
returning a Lua table with
the similar metrics. All returned values are presented as numbers with cast to
double, so there is a corresponding precision loss.
How to use
This section describes small example of metrics usage.
For example amount of strhash_misses
can be shown for tracking of new string
objects allocations. For example if we add code like:
local function sharded_storage_func(storage_name, func_name)
return 'sharded_storage.storages.' .. storage_name .. '.' .. func_name
end
increase in slope curve of strhash_misses
means, that after your changes
there are more new strings allocating at runtime. Of course slope curve of
strhash_misses
should be less than slope curve of strhash_hits
.
Slope curves of gc_freed
and gc_allocated
can be used for analysis of GC
pressure of your application (less is better).
Also we can check some hacky optimization with these metrics. For example let's
assume that we have this code snippet:
local old_metrics = misc.getmetrics()
local t = {}
for i = 1, 513 do
t[i] = i
end
local new_metrics = misc.getmetrics()
local diff = new_metrics.gc_allocated - old_metrics.gc_allocated
diff
equals to 18879 after running of this chunk.
But if we change table initialization to
local table_new = require "table.new"
local old_metrics = misc.getmetrics()
local t = table_new(513,0)
for i = 1, 513 do
t[i] = i
end
local new_metrics = misc.getmetrics()
local diff = new_metrics.gc_allocated - old_metrics.gc_allocated
diff
shows us only 5895.
Slope curves of gc_steps_*
can be used for tracking of GC pressure too. For
long time observations you will see periodic increment for gc_steps_*
metrics
-- for example longer period of gc_steps_atomic
increment is better. Also
additional amount of gc_steps_propagate
in one period can be used to
indirectly estimate amount of objects. These values also correlate with the
step multiplier of the GC. The amount of incremental steps can grow, but
one step can process a small amount of objects. So these metrics should be
considered together with GC setup.
Amount of gc_*num
is useful for control of memory leaks -- total amount of
these objects should not growth nonstop (you can also track gc_total
for
this). Also jit_mcode_size
can be used for tracking amount of allocated
memory for traces machine code.
Slope curves of jit_trace_abort
shows how many times trace hasn't been
compiled when the attempt was made (less is better).
Amount of gc_trace_num
is shown how much traces was generated (usually
more is better).
And the last one -- gc_snap_restores
can be used for estimation when LuaJIT
is stop trace execution. If slope curves of this metric growth after changing
old code it can mean performance degradation.
Assumes that we have code like this:
local function foo(i)
return i <= 5 and i or tostring(i)
end
-- minstitch option needs to emulate nonstitching behaviour
jit.opt.start(0, "hotloop=2", "hotexit=2", "minstitch=15")
local sum = 0
local old_metrics = misc.getmetrics()
for i = 1, 10 do
sum = sum + foo(i)
end
local new_metrics = misc.getmetrics()
local diff = new_metrics.jit_snap_restore - old_metrics.jit_snap_restore
diff
equals 3 (1 side exit on loop end, 2 side exits to the interpreter
before trace gets hot and compiled) after this chunk of code.
And now we decide to change foo
function like this:
local function foo(i)
-- math.fmod is not yet compiled!
return i <= 5 and i or math.fmod(i, 11)
end
diff
equals 6 (1 side exit on loop end, 2 side exits to the interpreter
before trace gets hot and compiled an 3 side exits from the root trace could
not get compiled) after the same chunk of code.