Skip to content

[5pt] Document LuaJIT getmetrics C and Lua API #1597

@Buristan

Description

@Buristan

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.

Metadata

Metadata

Labels

featureA new functionalityserver[area] Task relates to Tarantool's server (core) functionality

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions