Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

♻ KeyCache-Garbage Collector #2186

Merged
merged 7 commits into from
Nov 7, 2021
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions A3-Antistasi/MissionDescription/CfgRemoteExec.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,15 @@ class CfgRemoteExec {
jip = 1; // 0: No JIP, 1: JIP Allowed
allowedTargets = 0; // 0: All machines, 1: Only to other clients, 2: Only to server

class A3A_fnc_keyCache_delete { mode = 0; };
class A3A_fnc_keyCache_garbageCollector { mode = 0; };
class A3A_fnc_keyCache_has { mode = 0; };
class A3A_fnc_keyCache_init { mode = 0; };
class A3A_fnc_keyCache_lookup { mode = 0; };
class A3A_fnc_keyCache_registerForGC { mode = 0; };
class A3A_fnc_keyCache_set { mode = 0; };
class A3A_fnc_keyCache_startGarbageCollectors { mode = 0; };
class A3A_fnc_keyCache_translate { mode = 0; };
};

// List of Commands allowed to be sent from client via remoteExec
Expand Down
13 changes: 13 additions & 0 deletions A3-Antistasi/functions.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -347,6 +347,19 @@ class A3A
class itemset_miscEssentials {};
};

class KeyCache {
file = "functions\Utility\KeyCache";
class keyCache_delete {};
class keyCache_garbageCollector {};
class keyCache_has {};
class keyCache_init {};
class keyCache_lookup {};
class keyCache_registerForGC {};
class keyCache_set {};
class keyCache_startGarbageCollectors {};
class keyCache_translate {};
};

class Loadouts
{
file = "functions\Templates\Loadouts";
Expand Down
16 changes: 16 additions & 0 deletions A3-Antistasi/functions/Utility/KeyCache/Tests/readme.sqf
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
/*
Run in order.
Make sure previous test finishes before starting next.
If the previous test failed, it's likely that the following will too.
*/

// Recommended
call compileScript ["functions\Utility\KeyCache\Tests\unitTest_garbageCollector_basicPromotion.sqf"];
call compileScript ["functions\Utility\KeyCache\Tests\unitTest_garbageCollector_timedPromotion.sqf"];
call compileScript ["functions\Utility\KeyCache\Tests\unitTest_garbageCollector_basicDeletion.sqf"];
// Will take ≈10 minutes. Game will still run at 60fps.
call compileScript ["functions\Utility\KeyCache\Tests\unitTest_garbageCollector_shortFpsStressTest.sqf"];

// Not recommenced for common testing
// Will take ≈2 hours 46 minutes. Game will still run at 60fps.
call compileScript ["functions\Utility\KeyCache\Tests\unitTest_garbageCollector_longFpsStressTest.sqf"];
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@

#include "..\config.hpp"
#include "..\..\..\..\Includes\common.inc"
FIX_LINE_NUMBERS()

__keyCache_setVar(A3A_keyCache_DB, nil);
__keyCache_setVar(A3A_keyCache_defaultTTL, nil);
__keyCache_setVar(A3A_keyCache_GC_minSpanSize, nil);
__keyCache_setVar(A3A_keyCache_GC_generations, nil);
__keyCache_setVar(A3A_keyCache_GC_gen0NewestBucket, nil);
__keyCache_setVar(A3A_keyCache_init, nil);
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@

#include "..\config.hpp"
#include "..\..\..\..\Includes\common.inc"
FIX_LINE_NUMBERS()

{ terminate _x; } forEach __keyCache_getVar(A3A_keyCache_garbageCollectorHandles);
__keyCache_setVar(A3A_keyCache_garbageCollection, nil);
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
// call compileScript ["functions\Utility\KeyCache\Tests\unitTest_garbageCollector_basicDeletion.sqf"];

#include "..\config.hpp"
#include "..\..\..\..\Includes\common.inc"
FIX_LINE_NUMBERS()

private _reporterContext = [];
private _fnc_reporter = {
params ["_context","_text"];
["UnitTest KeyCache-GC", _text] call A3A_fnc_customHint;
Info("UnitTest | KeyCache GarbageCollector | " + _text);
};
A3A_keyCache_unitTest_directoryPath = "functions\Utility\KeyCache\Tests\";


if (!isNil {Dev_unitTestInProgress}) exitWith {
Error("Previous unit test still running");
"Previous unit test still running";
};
Dev_unitTestInProgress = true;
Dev_basicDeletionTestHandle = [_fnc_reporter,_reporterContext] spawn {
//// Setup
params ["_fnc_reporter","_reporterContext"];
"confirmUnitTest" call A3A_fnc_keyCache_init;

private _keyCache_DB = __keyCache_getVar(A3A_keyCache_DB);
_keyCache_DB set ["Test123", [
"value",
100,
0
]];

private _keyCache_GC_gen0NewestBucket = [];
__keyCache_setVar(A3A_keyCache_GC_gen0NewestBucket, _keyCache_GC_gen0NewestBucket);
private _keyCache_GC_generations = [
[ // Gen0
[_keyCache_GC_gen0NewestBucket],
_keyCache_GC_gen0NewestBucket,
0.001,
1,
0
]
];
__keyCache_setVar(A3A_keyCache_GC_generations, _keyCache_GC_generations);

private _GCHandle = [0] spawn A3A_fnc_keyCache_garbageCollector;
uiSleep 1;
"Test123" call A3A_fnc_keyCache_registerForGC;
[_reporterContext, "Basic Deletion<br/>Test Started"] call _fnc_reporter;

private _timeout = serverTime + 30;
private _passedTest = false;

//// Assert
waitUntil {
_passedTest = !("Test123" in _keyCache_DB);
_passedTest || _timeout <= serverTime
};
if (_passedTest) then {
[_reporterContext, "Basic Deletion<br/>Test Passed"] call _fnc_reporter;
} else {
[_reporterContext, "Basic Deletion<br/>Test Failed"] call _fnc_reporter;
};

//// Clean Up
terminate _GCHandle;
call compileScript [A3A_keyCache_unitTest_directoryPath+"unitTestUtility_revertInit.sqf"];
Dev_unitTestInProgress = nil;
};
"Unit Test Started";
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
// call compileScript ["functions\Utility\KeyCache\Tests\unitTest_garbageCollector_basicPromotion.sqf"];

#include "..\config.hpp"
#include "..\..\..\..\Includes\common.inc"
FIX_LINE_NUMBERS()

private _reporterContext = [];
private _fnc_reporter = {
params ["_context","_text"];
["UnitTest KeyCache-GC", _text] call A3A_fnc_customHint;
Info("UnitTest | KeyCache GarbageCollector | " + _text);
};
A3A_keyCache_unitTest_directoryPath = "functions\Utility\KeyCache\Tests\";


if (!isNil {Dev_unitTestInProgress}) exitWith {
Error("Previous unit test still running");
"Previous unit test still running";
};
Dev_unitTestInProgress = true;
Dev_basicPromotionTestHandle = [_fnc_reporter,_reporterContext] spawn {
//// Setup
params ["_fnc_reporter","_reporterContext"];
"confirmUnitTest" call A3A_fnc_keyCache_init;

private _keyCache_DB = __keyCache_getVar(A3A_keyCache_DB);
_keyCache_DB set ["Test123", [
"value",
100,
serverTime + 100
]];

private _keyCache_GC_gen0NewestBucket = [];
__keyCache_setVar(A3A_keyCache_GC_gen0NewestBucket, _keyCache_GC_gen0NewestBucket);
private _gen1TopBucket = [];

// _x params ["_allBuckets","_newestBucket","_totalPeriod","_bucketsAmount","_promotedGeneration"]; // <ARRAY>, <ARRAY>, <SCALAR>, <SCALAR>, <SCALAR>
private _keyCache_GC_generations = [
[ // Gen0
[_keyCache_GC_gen0NewestBucket],
_keyCache_GC_gen0NewestBucket,
0.001,
1,
1
],
[ // Gen1
[_gen1TopBucket],
_gen1TopBucket,
999,
1,
1
]
];
__keyCache_setVar(A3A_keyCache_GC_generations, _keyCache_GC_generations);

private _GCHandle = [0] spawn A3A_fnc_keyCache_garbageCollector;
uiSleep 1;
"Test123" call A3A_fnc_keyCache_registerForGC;
[_reporterContext, "Basic Promotion<br/>Test Started"] call _fnc_reporter;


private _timeout = serverTime + 30;
private _passedTest = false;

//// Assert
waitUntil {
_passedTest = "Test123" in _gen1TopBucket;
_passedTest || _timeout <= serverTime
};
if (_passedTest) then {
[_reporterContext, "Basic Promotion<br/>Test Passed"] call _fnc_reporter;
} else {
[_reporterContext, "Basic Promotion<br/>Test Failed"] call _fnc_reporter;
};

//// Clean Up
terminate _GCHandle;
call compileScript [A3A_keyCache_unitTest_directoryPath+"unitTestUtility_revertInit.sqf"];
Dev_unitTestInProgress = nil;
};
"Unit Test Started";
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
// call compileScript ["functions\Utility\KeyCache\Tests\unitTest_garbageCollector_longFpsStressTest.sqf"];

#include "..\config.hpp"
#include "..\..\..\..\Includes\common.inc"
FIX_LINE_NUMBERS()

private _reporterContext = [];
private _fnc_reporter = {
params ["_context","_text"];
["UnitTest KeyCache-GC", _text] call A3A_fnc_customHint;
Info("UnitTest | KeyCache GarbageCollector | " + _text);
};
A3A_keyCache_unitTest_directoryPath = "functions\Utility\KeyCache\Tests\";

if (!isNil {Dev_unitTestInProgress}) exitWith {
Error("Previous unit test still running");
"Previous unit test still running";
};
Dev_unitTestInProgress = true;
Dev_longFpsStressTestHandle = [_fnc_reporter,_reporterContext] spawn {
//// Setup
params ["_fnc_reporter","_reporterContext"];

private _fpsLog = [];
private _fnc_logFPS = {
params ["_group","_details"];
_fpsLog pushBack [serverTime, _group, _details, diag_fps, (count _keyCache_DB) toFixed 0];
[_reporterContext, "Long FPS Stress Test<br/>" + _group + "<br/>" + _details] call _fnc_reporter
};

"confirmUnitTest" call A3A_fnc_keyCache_init;
private _keyCache_GC_generations = __keyCache_getVar(A3A_keyCache_GC_generations);
_keyCache_GC_generations #0 set [2,10];
_keyCache_GC_generations #1 set [2,30];
_keyCache_GC_generations #2 set [2,90];

private _keyCache_DB = __keyCache_getVar(A3A_keyCache_DB);
["GivingGCsIdleTime", ""] call _fnc_logFPS;

"confirmUnitTest" call A3A_fnc_keyCache_startGarbageCollectors;
// Ensure that all GCs are in idle state.
uiSleep 10;
["BaselineTaken", ""] call _fnc_logFPS;


private _amountOfMillionStressItems = 69;
private _stressItemTotal = "/"+ (_amountOfMillionStressItems toFixed 1) +" Million";
private _randomTTLsWeighted = [5,0.75, 15,0.20, 45,0.05];
for "_j" from 0 to 10*_amountOfMillionStressItems -1 step 1 do {
for "_k" from 0 to 100000-1 step 10000 do {
for "_l" from _k to _k + 10000-1 do {
private _name = (str _j) + (_l toFixed 0);
private _TTL = selectRandomWeighted _randomTTLsWeighted;
_keyCache_DB set [_name, [nil,_TTL,serverTime + _TTL]];
_name call A3A_fnc_keyCache_registerForGC;
};
//uiSleep 0.01; Processed at max speed anyway.
};
["StressItem", (_j/10 toFixed 1)+_stressItemTotal] call _fnc_logFPS;
};

private _timeResolution = 5;
private _waitTime = 180;
private _endTime = serverTime + _waitTime;
waitUntil {
["PostStressWatch", ((_endTime - serverTime) toFixed 0) + " sec remaining"] call _fnc_logFPS;
_endTime <= serverTime || {uiSleep _timeResolution; false};
};
//// Assert
private _passedTest = count _keyCache_DB == 0;
if (_passedTest) then {
["TestPassed","Data copied to clipboard"] call _fnc_logFPS;
} else {
["TestFailed","Data copied to clipboard"] call _fnc_logFPS;
};
copyToClipboard str _fpsLog;

//// Clean Up
call compileScript [A3A_keyCache_unitTest_directoryPath+"unitTestUtility_revertInit.sqf"];
call compileScript [A3A_keyCache_unitTest_directoryPath+"unitTestUtility_revertStartGarbageCollectors.sqf"];
Dev_unitTestInProgress = nil;
};
"Unit Test Started";
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
// call compileScript ["functions\Utility\KeyCache\Tests\unitTest_garbageCollector_shortFpsStressTest.sqf"];

#include "..\config.hpp"
#include "..\..\..\..\Includes\common.inc"
FIX_LINE_NUMBERS()

private _reporterContext = [];
private _fnc_reporter = {
params ["_context","_text"];
["UnitTest KeyCache-GC", _text] call A3A_fnc_customHint;
Info("UnitTest | KeyCache GarbageCollector | " + _text);
};
A3A_keyCache_unitTest_directoryPath = "functions\Utility\KeyCache\Tests\";

if (!isNil {Dev_unitTestInProgress}) exitWith {
Error("Previous unit test still running");
"Previous unit test still running";
};
Dev_unitTestInProgress = true;
Dev_shortFpsStressTestHandle = [_fnc_reporter,_reporterContext] spawn {
//// Setup
params ["_fnc_reporter","_reporterContext"];

private _fpsLog = [];
private _fnc_logFPS = {
params ["_group","_details"];
_fpsLog pushBack [serverTime, _group, _details, diag_fps, (count _keyCache_DB) toFixed 0];
[_reporterContext, "Short FPS Stress Test<br/>" + _group + "<br/>" + _details] call _fnc_reporter
};

"confirmUnitTest" call A3A_fnc_keyCache_init;
private _keyCache_GC_generations = __keyCache_getVar(A3A_keyCache_GC_generations);
_keyCache_GC_generations #0 set [2,10];
_keyCache_GC_generations #1 set [2,30];
_keyCache_GC_generations #2 set [2,90];

private _keyCache_DB = __keyCache_getVar(A3A_keyCache_DB);
["GivingGCsIdleTime", ""] call _fnc_logFPS;

"confirmUnitTest" call A3A_fnc_keyCache_startGarbageCollectors;
// Ensure that all GCs are in idle state.
uiSleep 10;
["BaselineTaken", ""] call _fnc_logFPS;


private _amountOfMillionStressItems = 5;
private _stressItemTotal = "/"+ (_amountOfMillionStressItems toFixed 1) +" Million";
private _randomTTLsWeighted = [5,0.75, 15,0.20, 45,0.05];
for "_j" from 0 to 10*_amountOfMillionStressItems-1 step 1 do {
for "_k" from 0 to 100000-1 step 10000 do {
for "_l" from _k to _k + 10000-1 do {
private _name = (str _j) + (_l toFixed 0);
private _TTL = selectRandomWeighted _randomTTLsWeighted;
_keyCache_DB set [_name, [nil,_TTL,serverTime + _TTL]];
_name call A3A_fnc_keyCache_registerForGC;
};
//uiSleep 0.01; Processed at max speed anyway.
};
["StressItem", (_j/10 toFixed 1)+_stressItemTotal] call _fnc_logFPS;
};

private _timeResolution = 5;
private _waitTime = 80;
private _endTime = serverTime + _waitTime;
waitUntil {
["PostStressWatch", ((_endTime - serverTime) toFixed 0) + " sec remaining"] call _fnc_logFPS;
_endTime <= serverTime || {uiSleep _timeResolution; false};
};
//// Assert
private _passedTest = count _keyCache_DB == 0;
if (_passedTest) then {
["TestPassed","Data copied to clipboard"] call _fnc_logFPS;
} else {
["TestFailed","Data copied to clipboard"] call _fnc_logFPS;
};
copyToClipboard str _fpsLog;

//// Clean Up
call compileScript [A3A_keyCache_unitTest_directoryPath+"unitTestUtility_revertInit.sqf"];
call compileScript [A3A_keyCache_unitTest_directoryPath+"unitTestUtility_revertStartGarbageCollectors.sqf"];
Dev_unitTestInProgress = nil;
};
"Unit Test Started";
Loading