Permalink
Browse files

fix #6080

add knobs
  • Loading branch information...
rt
rt committed Nov 26, 2018
1 parent 5845552 commit 82a205f31a4ce8dc71925a06cd5cd87edc6125bc
@@ -1,3 +1,5 @@
/* This file is part of the Spring engine (GPL v2 or later), see LICENSE.html */
#ifndef SPRING_LUA_ALLOC_STATE_H
#define SPRING_LUA_ALLOC_STATE_H
@@ -4,6 +4,7 @@
#define LUA_CONTEXT_DATA_H
#include "Lua/LuaAllocState.h"
#include "Lua/LuaGarbageCollectCtrl.h"
#include "LuaMemPool.h"
#if (!defined(UNITSYNC) && !defined(DEDICATED))
#include "LuaShaders.h"
@@ -96,6 +97,7 @@ struct luaContextData {
int selectTeam;
SLuaAllocState allocState;
SLuaGarbageCollectCtrl gcCtrl;
#if (!defined(UNITSYNC) && !defined(DEDICATED))
// NOTE:
@@ -0,0 +1,26 @@
/* This file is part of the Spring engine (GPL v2 or later), see LICENSE.html */
#ifndef SPRING_LUA_GARBAGE_COLLECT_CTRL_H
#define SPRING_LUA_GARBAGE_COLLECT_CTRL_H
#include <limits>
struct SLuaGarbageCollectCtrl {
// maximum number of lua_gc calls made in each CollectGarbage loop
int itersPerBatch = std::numeric_limits<int>::max();
// number of steps executed by a single lua_gc call
int numStepsPerIter = 10;
int minStepsPerIter = 1;
int maxStepsPerIter = 10000;
// CollectGarbage loop runtime bounds, in milliseconds
float minLoopRunTime = 0.0f;
float maxLoopRunTime = 100.0f;
float baseRunTimeMult = 0.0f;
float baseMemLoadMult = 0.0f;
};
#endif
@@ -52,6 +52,10 @@
#include <string>
CONFIG(float, LuaGarbageCollectionMemLoadMult).defaultValue(1.33f).minimumValue(1.0f).maximumValue(100.0f);
CONFIG(float, LuaGarbageCollectionRunTimeMult).defaultValue(5.0f).minimumValue(1.0f).description("in milliseconds");
static spring::unsynced_set<const lua_State*> SYNCED_LUAHANDLE_STATES;
static spring::unsynced_set<const lua_State*> UNSYNCED_LUAHANDLE_STATES;
const spring::unsynced_set<const lua_State*>* LUAHANDLE_STATES[2] = {&UNSYNCED_LUAHANDLE_STATES, &SYNCED_LUAHANDLE_STATES};
@@ -94,13 +98,17 @@ CLuaHandle::CLuaHandle(const string& _name, int _order, bool _userMode, bool _sy
, killMe(false)
// no shared pool for LuaIntro to protect against LoadingMT=1
// do not use it for LuaMenu either; too many blocks allocated
// by *other* states end up not being recycled so we clear the
// shared pool on reload
// by *other* states end up not being recycled which presently
// forces clearing the shared pool on reload
, D(_name != "LuaIntro" && name != "LuaMenu", true)
, callinErrors(0)
{
D.owner = this;
D.synced = _synced;
D.gcCtrl.baseMemLoadMult = configHandler->GetFloat("LuaGarbageCollectionMemLoadMult");
D.gcCtrl.baseRunTimeMult = configHandler->GetFloat("LuaGarbageCollectionRunTimeMult");
L = LUA_OPEN(&D);
LUA_INSERT_STATE(L, LUAHANDLE_STATES[D.synced]);
@@ -2475,54 +2483,49 @@ void CLuaHandle::DownloadProgress(int ID, long downloaded, long total)
/******************************************************************************/
/******************************************************************************/
CONFIG(float, LuaGarbageCollectionMemLoadMult).defaultValue(1.33f).minimumValue(1.0f).maximumValue(100.0f);
CONFIG(float, LuaGarbageCollectionRunTimeMult).defaultValue(5.0f).minimumValue(1.0f).description("in milliseconds");
void CLuaHandle::CollectGarbage()
{
static const float gcMemLoadMult = configHandler->GetFloat("LuaGarbageCollectionMemLoadMult");
static const float gcRunTimeMult = configHandler->GetFloat("LuaGarbageCollectionRunTimeMult");
const float gcMemLoadMult = D.gcCtrl.baseMemLoadMult;
const float gcRunTimeMult = D.gcCtrl.baseRunTimeMult;
if (spring_lua_alloc_skip_gc(gcMemLoadMult))
return;
lua_lock(L_GC);
SetHandleRunning(L_GC, true);
// note: total footprint INCLUDING garbage
int luaMemFootPrintKB = lua_gc(L_GC, LUA_GCCOUNT, 0);
int gcItersInBatch = 0;
static int gcStepsPerIter = 10;
// note: total footprint INCLUDING garbage, in KB
int gcMemFootPrint = lua_gc(L_GC, LUA_GCCOUNT, 0);
int gcItersInBatch = 0;
int& gcStepsPerIter = D.gcCtrl.numStepsPerIter;
// if gc runs at a fixed rate, the upper limit to base runtime will
// quickly be reached since Lua's footprint can easily exceed 100MB
// and OOM exceptions become a concern when catching up
// OTOH if gc is tied to sim-speed the increased number of calls can
// mean too much time is spent on it, must weigh the per-call period
const float gcSpeedFactor = Clamp(gs->speedFactor * (1 - gs->PreSimFrame()) * (1 - gs->paused), 1.0f, 50.0f);
const float gcBaseRunTime = smoothstep(10.0f, 100.0f, luaMemFootPrintKB / 1024);
const float gcLoopRunTime = (gcBaseRunTime * gcRunTimeMult) / gcSpeedFactor;
const float gcBaseRunTime = smoothstep(10.0f, 100.0f, gcMemFootPrint / 1024);
const float gcLoopRunTime = Clamp((gcBaseRunTime * gcRunTimeMult) / gcSpeedFactor, D.gcCtrl.minLoopRunTime, D.gcCtrl.maxLoopRunTime);
const spring_time startTime = spring_gettime();
const spring_time endTime = startTime + spring_msecs(gcLoopRunTime);
// collect garbage until time runs out
while (spring_gettime() < endTime) {
// perform GC cycles until time runs out or iteration-limit is reached
while (gcItersInBatch < D.gcCtrl.itersPerBatch && spring_gettime() < endTime) {
gcItersInBatch++;
if (!lua_gc(L_GC, LUA_GCSTEP, gcStepsPerIter))
continue;
// garbage-collection cycle finished
const int luaMemFootPrintNow = lua_gc(L_GC, LUA_GCCOUNT, 0);
const int luaMemFootPrintDif = luaMemFootPrintNow - luaMemFootPrintKB;
const int gcMemFootPrintNow = lua_gc(L_GC, LUA_GCCOUNT, 0);
const int gcMemFootPrintDif = gcMemFootPrintNow - gcMemFootPrint;
luaMemFootPrintKB = luaMemFootPrintNow;
gcMemFootPrint = gcMemFootPrintNow;
// early-exit if cycle didn't free any memory
if (luaMemFootPrintDif == 0)
if (gcMemFootPrintDif == 0)
break;
}
@@ -2536,10 +2539,11 @@ void CLuaHandle::CollectGarbage()
if (gcStepsPerIter > 1 && gcItersInBatch > 0) {
// runtime optimize number of steps to process in a batch
const float avgTimePerLoopIter = (finishTime - startTime).toMilliSecsf() / gcItersInBatch;
const float avgLoopIterTime = (finishTime - startTime).toMilliSecsf() / gcItersInBatch;
gcStepsPerIter -= (avgTimePerLoopIter > (gcRunTimeMult * 0.150f));
gcStepsPerIter += (avgTimePerLoopIter < (gcRunTimeMult * 0.075f));
gcStepsPerIter -= (avgLoopIterTime > (gcRunTimeMult * 0.150f));
gcStepsPerIter += (avgLoopIterTime < (gcRunTimeMult * 0.075f));
gcStepsPerIter = Clamp(gcStepsPerIter, D.gcCtrl.minStepsPerIter, D.gcCtrl.maxStepsPerIter);
}
eventHandler.DbgTimingInfo(TIMING_GC, startTime, finishTime);
@@ -257,6 +257,7 @@ bool LuaUnsyncedCtrl::PushEntries(lua_State* L)
REGISTER_LUA_CFUNC(SetLogSectionFilterLevel);
REGISTER_LUA_CFUNC(ClearWatchDogTimer);
REGISTER_LUA_CFUNC(GarbageCollectCtrl);
REGISTER_LUA_CFUNC(PreloadUnitDefModel);
REGISTER_LUA_CFUNC(PreloadFeatureDefModel);
@@ -2899,6 +2900,25 @@ int LuaUnsyncedCtrl::ClearWatchDogTimer(lua_State* L) {
return 0;
}
int LuaUnsyncedCtrl::GarbageCollectCtrl(lua_State* L) {
luaContextData* ctxData = GetLuaContextData(L);
SLuaGarbageCollectCtrl& gcCtrl = ctxData->gcCtrl;
gcCtrl.itersPerBatch = std::max(0, luaL_optint(L, 1, gcCtrl.itersPerBatch));
gcCtrl.numStepsPerIter = std::max(0, luaL_optint(L, 2, gcCtrl.numStepsPerIter));
gcCtrl.minStepsPerIter = std::max(0, luaL_optint(L, 3, gcCtrl.minStepsPerIter));
gcCtrl.maxStepsPerIter = std::max(0, luaL_optint(L, 4, gcCtrl.maxStepsPerIter));
gcCtrl.minLoopRunTime = std::max(0.0f, luaL_optfloat(L, 5, gcCtrl.minLoopRunTime));
gcCtrl.maxLoopRunTime = std::max(0.0f, luaL_optfloat(L, 6, gcCtrl.maxLoopRunTime));
gcCtrl.baseRunTimeMult = std::max(0.0f, luaL_optfloat(L, 7, gcCtrl.baseRunTimeMult));
gcCtrl.baseMemLoadMult = std::max(0.0f, luaL_optfloat(L, 8, gcCtrl.baseMemLoadMult));
return 0;
}
/******************************************************************************/
/******************************************************************************/
@@ -163,6 +163,7 @@ class LuaUnsyncedCtrl {
static int SetLogSectionFilterLevel(lua_State* L);
static int ClearWatchDogTimer(lua_State* L);
static int GarbageCollectCtrl(lua_State* L);
static int PreloadUnitDefModel(lua_State* L);
static int PreloadFeatureDefModel(lua_State* L);
@@ -136,7 +136,8 @@ using std::string;
using std::vector;
static void flush_exit(int ec) {
// exit(1) is not a normal termination, force a flush
// exit(1) is supposed to be a normal termination, but force a flush
std::fprintf(stdout, "[gflags::%s]\n", __func__);
std::fflush(stdout);
std::exit(ec);
}
@@ -304,8 +304,10 @@ void spring_lua_alloc_get_stats(SLuaAllocState* state)
bool spring_lua_alloc_skip_gc(float gcLoadMult)
{
// if memory load is smaller than 1/gcLoadMult run the GC less frequently
return (lguRNG.NextFloat() > (gcLoadMult * float(gLuaAllocState.allocedBytes.load()) / float(SLuaAllocState::maxAllocedBytes)));
// randomly skip a GC cycle with probability 1 - (weighted memory load ratio)
const float rawLoadRatio = float(gLuaAllocState.allocedBytes.load()) / float(SLuaAllocState::maxAllocedBytes);
const float modLoadRatio = gcLoadMult * rawLoadRatio;
return (lguRNG.NextFloat() > modLoadRatio);
}
bool spring_lua_alloc_get_error(SLuaAllocError* error)

0 comments on commit 82a205f

Please sign in to comment.