Lunatic: add 'stat' module for running statistics.

Also, rewrite the mapastats iterator and the LIZTROOP hitscan timing
in terms of that.

git-svn-id: https://svn.eduke32.com/eduke32@2858 1a8010ca-5511-0410-912e-c29ae57300e0
This commit is contained in:
helixhorned 2012-08-02 10:52:32 +00:00
parent fdfcca557b
commit a00f411b3b
7 changed files with 114 additions and 18 deletions

View file

@ -167,7 +167,8 @@ ifneq (0,$(LUNATIC))
GAMEOBJS+= $(OBJ)/luaJIT_BC_con_lang.$o \ GAMEOBJS+= $(OBJ)/luaJIT_BC_con_lang.$o \
$(OBJ)/luaJIT_BC_lunacon.$o \ $(OBJ)/luaJIT_BC_lunacon.$o \
$(OBJ)/luaJIT_BC_geom.$o \ $(OBJ)/luaJIT_BC_geom.$o \
$(OBJ)/luaJIT_BC_randgen.$o $(OBJ)/luaJIT_BC_randgen.$o \
$(OBJ)/luaJIT_BC_stat.$o
# now, take care of having the necessary symbols (sector, wall, etc.) in the # now, take care of having the necessary symbols (sector, wall, etc.) in the
# executable no matter what the debugging level # executable no matter what the debugging level

View file

@ -547,6 +547,7 @@ local allowed_modules = {
randgen = require("randgen"), randgen = require("randgen"),
geom = require("geom"), geom = require("geom"),
stat = require("stat"),
} }
local package_loaded = {} local package_loaded = {}

View file

@ -42,6 +42,7 @@ luaJIT_BC_lunacon;
luaJIT_BC_con_lang; luaJIT_BC_con_lang;
luaJIT_BC_geom; luaJIT_BC_geom;
luaJIT_BC_randgen; luaJIT_BC_randgen;
luaJIT_BC_stat;
rand_jkiss_u32; rand_jkiss_u32;
rand_jkiss_dbl; rand_jkiss_dbl;

View file

@ -1,4 +1,4 @@
#!/usr/local/bin/luajit #!/usr/bin/env luajit
-- Generic map iterator. -- Generic map iterator.

View file

@ -6,6 +6,10 @@ local string = require "string"
local math = require "math" local math = require "math"
local print = print local print = print
local type = type
local stat = require "stat"
module(...) module(...)
@ -14,37 +18,29 @@ local function printf(fmt, ...)
print(string.format(fmt, ...)) print(string.format(fmt, ...))
end end
local N = 0
local sumnumsectors = 0 local sumnumsectors = 0
local sumnumwalls = 0 local sumnumwalls = 0
local sumratio = 0 local s = stat.new()
local sumsqratio = 0
function success(map, fn) function success(map, fn)
local ns = map.numsectors local ns = map.numsectors
local nw = map.numwalls local nw = map.numwalls
N = N+1 s:add(nw/ns)
sumratio = sumratio + nw/ns
sumsqratio = sumsqratio + (nw/ns)^2
sumnumsectors = sumnumsectors+ns sumnumsectors = sumnumsectors+ns
sumnumwalls = sumnumwalls+nw sumnumwalls = sumnumwalls+nw
end end
function finish() function finish()
printf("%d maps\n", N) res = s:getstats()
printf("%d maps\n", res.n)
printf("total sectors: %d", sumnumsectors) printf("total sectors: %d", sumnumsectors)
printf("total walls: %d", sumnumwalls) printf("total walls: %d", sumnumwalls)
printf("total walls / total sectors: %.02f", sumnumwalls/sumnumsectors) printf("total walls / total sectors: %.02f", sumnumwalls/sumnumsectors)
printf("") printf("")
printf("Walls/sector") printf("Walls/sector")
print(res)
local meanwpers = sumratio/N
printf(" mean: %.02f", meanwpers)
printf(" stdev: %.02f", math.sqrt(sumsqratio/N - meanwpers^2))
end end

View file

@ -0,0 +1,89 @@
-- Statistics module for Lunatic.
local ffi = require("ffi")
local math = require("math")
local string = require("string")
module(...)
ffi.cdef[[
typedef struct {
double n;
double m, s;
double min, max;
} runningstat_t;
typedef struct {
const double n;
const double mean, var, std;
const double min, max;
} runningstat_res_t;
]]
local res_mt = {
__tostring = function(s)
return
string.format("N=%d; mean=%.5g, std=%.5g; min=%.5g, max=%.5g",
s.n, s.mean, s.std, s.min, s.max)
end
}
local rstatres = ffi.metatype("runningstat_res_t", res_mt)
local mt = {
__tostring = function(s)
-- XXX: with this and the other serializing of ffi types, take care of
-- NaN and Infs when reading back (e.g. by "nan=0/0" in that context)
return "stat.new("..s.n..","..s.m..","..s.s..","..s.min..","..s.max..")"
end,
-- See: Accurately computing running variance, by John D. Cook
-- http://www.johndcook.com/standard_deviation.html
__index = {
add = function(s, num)
if (s.n > 0) then
-- N>0, recurrence
s.n = s.n+1
local lastm = s.m
s.m = lastm + (num-lastm)/s.n
s.s = s.s + (num-lastm)*(num-s.m)
if (num < s.min) then s.min = num end
if (num > s.max) then s.max = num end
else
-- N==0, initialization
s.n = 1
s.m = num
s.s = 0
s.min = num
s.max = num
end
end,
getstats = function(s)
local var = s.n > 1 and s.s/(s.n-1) or 0/0
return rstatres(s.n, s.m, var, math.sqrt(var), s.min, s.max)
end,
},
}
local rstat = ffi.metatype("runningstat_t", mt)
function new(n,m,s, min,max)
if (n == nil) then
-- initialization containing no elements
return rstat(0, 0, 0/0, 0/0, 0/0)
elseif (m == nil) then
-- same as initialization with N==0 above (one element)
return rstat(1, n, 0, n, n)
else
-- generic initialization (internal use only)
return rstat(n,m,s, min,max)
end
end

View file

@ -229,6 +229,9 @@ gameevent(gv.EVENT_ENTERLEVEL,
end end
) )
local stat = require("stat")
local hs = stat.new()
gameactor(1680, -- LIZTROOP gameactor(1680, -- LIZTROOP
function(i, playeri, dist) function(i, playeri, dist)
sprite[i].pal = math.random(32) sprite[i].pal = math.random(32)
@ -239,8 +242,13 @@ gameactor(1680, -- LIZTROOP
local x,y,z = spr.x, spr.y, spr.z local x,y,z = spr.x, spr.y, spr.z
local t = gv.gethitickms() local t = gv.gethitickms()
local hit = hitscan(x,y,z, spr.sectnum, 10, 10, 0, gv.CLIPMASK0) local hit = hitscan(x,y,z, spr.sectnum, 10, 10, 0, gv.CLIPMASK0)
printf("hitscan took %.03f us, sec=%d, wal=%d, spr=%d", 1000*(gv.gethitickms()-t),
hit.hitsect, hit.hitwall, hit.hitsprite) hs:add(1000*(gv.gethitickms()-t))
if (hs.n == 300) then
printf("hitscan: %s", tostring(hs:getstats()))
hs = stat.new()
end
end end
) )