From 3a32c1badd225f957fa4afd432346664b991076b Mon Sep 17 00:00:00 2001 From: jonpas Date: Wed, 3 Jul 2024 17:40:13 +0200 Subject: [PATCH] Revert "Main - Replace Fast Hash system with native hashmaps" (#1331) --- addons/main/CfgLocationTypes.hpp | 13 ++++ addons/main/XEH_PREP.hpp | 8 +++ addons/main/XEH_preInit.sqf | 25 +++++++ addons/main/config.cpp | 1 + addons/main/fnc_fastHashCopy.sqf | 39 +++++++++++ addons/main/fnc_fastHashCopyArray.sqf | 29 ++++++++ addons/main/fnc_fastHashCreate.sqf | 26 +++++++ addons/main/fnc_fastHashKeys.sqf | 20 ++++++ addons/main/fnc_garbageCollector.sqf | 98 +++++++++++++++++++++++++++ addons/main/fnc_hashMonitor.sqf | 30 ++++++++ addons/main/script_macros.hpp | 22 +++--- 11 files changed, 300 insertions(+), 11 deletions(-) create mode 100644 addons/main/CfgLocationTypes.hpp create mode 100644 addons/main/fnc_fastHashCopy.sqf create mode 100644 addons/main/fnc_fastHashCopyArray.sqf create mode 100644 addons/main/fnc_fastHashCreate.sqf create mode 100644 addons/main/fnc_fastHashKeys.sqf create mode 100644 addons/main/fnc_garbageCollector.sqf create mode 100644 addons/main/fnc_hashMonitor.sqf diff --git a/addons/main/CfgLocationTypes.hpp b/addons/main/CfgLocationTypes.hpp new file mode 100644 index 000000000..781396d34 --- /dev/null +++ b/addons/main/CfgLocationTypes.hpp @@ -0,0 +1,13 @@ +class CfgLocationTypes { + // For use in Fast Hashes ONLY! + class ACRE_FastHashNamespaceDummy { + name = ""; + drawStyle = "ACRE_RequiredDrawStyle"; // Any valid drawStyle causes FPS drop on map controls due to the amount of fast hashes + texture = ""; + color[] = {0,0,0,0}; + size = 0; + textSize = 0; + shadow = 0; + font = "PuristaMedium"; + }; +}; diff --git a/addons/main/XEH_PREP.hpp b/addons/main/XEH_PREP.hpp index fef2e8a79..09b46cede 100644 --- a/addons/main/XEH_PREP.hpp +++ b/addons/main/XEH_PREP.hpp @@ -1,2 +1,10 @@ +PREP(garbageCollector); +PREP(hashMonitor); + +PREP(fastHashCopy); +PREP(fastHashCopyArray); +PREP(fastHashCreate); +PREP(fastHashKeys); + PREP(dumpPerformanceCounters); PREP(dumpCallStack); diff --git a/addons/main/XEH_preInit.sqf b/addons/main/XEH_preInit.sqf index 3e6039fc8..c4ecd1ebf 100644 --- a/addons/main/XEH_preInit.sqf +++ b/addons/main/XEH_preInit.sqf @@ -11,4 +11,29 @@ ACRE_STACK_TRACE = []; ACRE_STACK_DEPTH = 0; ACRE_CURRENT_FUNCTION = ""; +// Fast Hashes +// All hash stuff must be in main to guarantee it is compiled and executed first for proper data handling. +if (isNil "ACRE_FAST_HASH_POOL") then { + ACRE_FAST_HASH_POOL = []; + for "_i" from 1 to 50000 do { + ACRE_FAST_HASH_POOL pushBack HASH_CREATE_NAMESPACE; + }; +}; +ACRE_FAST_HASH_TO_DELETE = []; + +[FUNC(hashMonitor), 0.33, []] call CBA_fnc_addPerFrameHandler; + +ACRE_FAST_HASH_CREATED_HASHES = []; +ACRE_FAST_HASH_VAR_STATE = (allVariables missionNamespace); +ACRE_FAST_HASH_VAR_LENGTH = count ACRE_FAST_HASH_VAR_STATE; +ACRE_FAST_HASH_GC_INDEX = 0; +ACRE_FAST_HASH_GC_FOUND_OBJECTS = []; +ACRE_FAST_HASH_GC_FOUND_ARRAYS = []; +ACRE_FAST_HASH_GC_CHECK_OBJECTS = []; +ACRE_FAST_HASH_CREATED_HASHES_NEW = []; +ACRE_FAST_HASH_GC_IGNORE = ["acre_fast_hash_gc_found_objects","acre_fast_hash_gc_found_arrays","acre_fast_hash_created_hashes","acre_fast_hash_gc_check_objects","acre_fast_hash_created_hashes_new","acre_fast_hash_var_state","acre_fast_hash_pool","acre_fast_hash_to_delete"]; +ACRE_FAST_HASH_GC_ORPHAN_CHECK_INDEX = 0; + +[FUNC(garbageCollector), 0.25, []] call CBA_fnc_addPerFrameHandler; + ADDON = true; diff --git a/addons/main/config.cpp b/addons/main/config.cpp index 8953c9eaf..aa424d33f 100644 --- a/addons/main/config.cpp +++ b/addons/main/config.cpp @@ -36,4 +36,5 @@ class CfgMods { #include "CfgSettings.hpp" #include "CfgEventHandlers.hpp" +#include "CfgLocationTypes.hpp" #include "CfgWeapons.hpp" diff --git a/addons/main/fnc_fastHashCopy.sqf b/addons/main/fnc_fastHashCopy.sqf new file mode 100644 index 000000000..5d9adc7c1 --- /dev/null +++ b/addons/main/fnc_fastHashCopy.sqf @@ -0,0 +1,39 @@ +#include "script_component.hpp" +/* + * Author: ACRE2Team + * Copies one hash to another or creates an ACRE2 hash with the object variables if the input is an object. + * + * Arguments: + * 0: ACRE 2 Hash / + * + * Return Value: + * ACRE 2 Hash + * + * Example: + * new_acreHash = [acreHash] call acre_main_fnc_fastHashCopy + * new_acreHash = [player] call acre_main_fnc_fastHashCopy + * + * Public: No + */ + +private _return = []; + +if (IS_ARRAY(_this)) then { + _return = _this call FUNC(fastHashCopyArray); +} else { + _return = call FUNC(fastHashCreate); + { + private _el = _this getVariable _x; + private _eln = _x; + if (IS_ARRAY(_el)) then { + _return setVariable [_eln, _el call FUNC(fastHashCopyArray)]; + } else { + if (IS_HASH(_el)) then { + _return setVariable [_eln, _el call FUNC(fastHashCopy)]; + } else { + _return setVariable [_eln, _el]; + }; + }; + } forEach (allVariables _this); +}; +_return; diff --git a/addons/main/fnc_fastHashCopyArray.sqf b/addons/main/fnc_fastHashCopyArray.sqf new file mode 100644 index 000000000..64654fad0 --- /dev/null +++ b/addons/main/fnc_fastHashCopyArray.sqf @@ -0,0 +1,29 @@ +#include "script_component.hpp" +/* + * Author: ACRE2Team + * Copies an array or an ACRE2 hash entry inside an ACRE 2 hash + * + * Arguments: + * 0: ACRE 2 Hash to be copied / + * + * Return Value: + * Extracted array of values to be copied + * + * Example: + * [["foo1", "foo2"]] call acre_main_fnc_fastHashCopyArray + * [acreHash] call acre_sys_core_fnc_fastHashCopyArray + * + * Public: No + */ + +_this apply { + if (IS_HASH(_x)) then { + (_x call FUNC(fastHashCopy)); + } else { + if (IS_ARRAY(_x)) then { + (_x call FUNC(fastHashCopyArray)); + } else { + _x; + }; + }; +}; diff --git a/addons/main/fnc_fastHashCreate.sqf b/addons/main/fnc_fastHashCreate.sqf new file mode 100644 index 000000000..3c1617982 --- /dev/null +++ b/addons/main/fnc_fastHashCreate.sqf @@ -0,0 +1,26 @@ +#include "script_component.hpp" +/* + * Author: ACRE2Team + * Creates an ACRE2 hash. This function can also be accessed through the macro HASH_CREATE. + * + * Arguments: + * None + * + * Return Value: + * ACRE2 Hash + * + * Example: + * [] call acre_main_fnc_fastHashCreate + * + * Public: No + */ + +if (ACRE_FAST_HASH_POOL isNotEqualTo []) exitWith { + private _ret = ACRE_FAST_HASH_POOL deleteAt 0; + ACRE_FAST_HASH_CREATED_HASHES_NEW pushBack _ret; + _ret +}; + +private _ret = HASH_CREATE_NAMESPACE; +ACRE_FAST_HASH_CREATED_HASHES_NEW pushBack _ret; +_ret diff --git a/addons/main/fnc_fastHashKeys.sqf b/addons/main/fnc_fastHashKeys.sqf new file mode 100644 index 000000000..a0ea15b3b --- /dev/null +++ b/addons/main/fnc_fastHashKeys.sqf @@ -0,0 +1,20 @@ +#include "script_component.hpp" +/* + * Author: ACRE2Team + * Retrieves a list of keys inside an ACRE 2 hash or variable names defined in an object. + * This function can be accessed through the macro HASH_KEYS. + * + * Arguments: + * 0: ACRE 2 Hash / + * + * Return Value: + * Array of keys inside an ACRE 2 Hash or object + * + * Example: + * keys = [acreHash] call acre_main_fnc_fastHashKeys + * keys = [player] call acre_main_fnc_fastHashKeys + * + * Public: No + */ + +(allVariables _this) select {!(isNil {_this getVariable _x})}; diff --git a/addons/main/fnc_garbageCollector.sqf b/addons/main/fnc_garbageCollector.sqf new file mode 100644 index 000000000..c9b81c4b9 --- /dev/null +++ b/addons/main/fnc_garbageCollector.sqf @@ -0,0 +1,98 @@ +#include "script_component.hpp" +/* + * Author: ACRE2Team + * ACRE2 garbage collector. Run as a per frame event handler. + * + * Arguments: + * Mone + * + * Return Value: + * None + * + * Example: + * [] call acre_main_fnc_garbageCollector + * + * Public: No + */ + +if (count ACRE_FAST_HASH_CREATED_HASHES_NEW < ((count ACRE_FAST_HASH_CREATED_HASHES)*0.1)/2) exitWith {}; +// diag_log text format["---------------------------------------------------"]; +private _init_time = diag_tickTime; +while {diag_tickTime - _init_time < 0.001 && {ACRE_FAST_HASH_GC_INDEX < ACRE_FAST_HASH_VAR_LENGTH}} do { + private _var_name = ACRE_FAST_HASH_VAR_STATE select ACRE_FAST_HASH_GC_INDEX; + private _x = missionNamespace getVariable [_var_name, nil]; + + ACRE_FAST_HASH_GC_INDEX = ACRE_FAST_HASH_GC_INDEX + 1; + if (!(_var_name in ACRE_FAST_HASH_GC_IGNORE)) then { + if (IS_HASH(_x)) then { + + ACRE_FAST_HASH_GC_FOUND_OBJECTS pushBack _x; + } else { + if (IS_ARRAY(_x)) then { + // diag_log text format["pushBack: %1: %2", _var_name, _x]; + ACRE_FAST_HASH_GC_FOUND_ARRAYS pushBack _x; + }; + }; + }; +}; +// diag_log text format["GC Objects Left: %1", ACRE_FAST_HASH_VAR_LENGTH - ACRE_FAST_HASH_GC_INDEX]; + +_init_time = diag_tickTime; +while {diag_tickTime - _init_time < 0.001 && {ACRE_FAST_HASH_GC_FOUND_ARRAYS isNotEqualTo []}} do { + private _array = ACRE_FAST_HASH_GC_FOUND_ARRAYS deleteAt 0; + { + if (IS_HASH(_x)) then { + // diag_log text format["pushBack: %1", _name]; + ACRE_FAST_HASH_GC_FOUND_OBJECTS pushBack _x; + } else { + if (IS_ARRAY(_x)) then { + // diag_log text format["pushBack sub-array: %1", _x]; + ACRE_FAST_HASH_GC_FOUND_ARRAYS pushBack _x; + }; + }; + } forEach _array; +}; +// diag_log text format["GC Arrays Left: %1", (count ACRE_FAST_HASH_GC_FOUND_ARRAYS)]; + +_init_time = diag_tickTime; +while {diag_tickTime - _init_time < 0.001 && {ACRE_FAST_HASH_GC_FOUND_OBJECTS isNotEqualTo []}} do { + private _hash = ACRE_FAST_HASH_GC_FOUND_OBJECTS deleteAt 0; + ACRE_FAST_HASH_GC_CHECK_OBJECTS pushBack _hash; + private _array = allVariables _hash; + { + _x = _hash getVariable _x; + if (IS_HASH(_x)) then { + ACRE_FAST_HASH_GC_FOUND_OBJECTS pushBack _x; + } else { + if (IS_ARRAY(_x)) then { + // diag_log text format["pushBack hash-array: %1", _x]; + ACRE_FAST_HASH_GC_FOUND_ARRAYS pushBack _x; + }; + }; + } forEach _array; +}; +// diag_log text format["GC Hashes Left: %1", (count ACRE_FAST_HASH_GC_FOUND_OBJECTS)]; + +if (ACRE_FAST_HASH_GC_INDEX >= ACRE_FAST_HASH_VAR_LENGTH && {ACRE_FAST_HASH_GC_FOUND_ARRAYS isEqualTo []} && {ACRE_FAST_HASH_GC_FOUND_OBJECTS isEqualTo []}) then { + if (ACRE_FAST_HASH_GC_ORPHAN_CHECK_INDEX < (count ACRE_FAST_HASH_CREATED_HASHES)) then { + _init_time = diag_tickTime; + while {diag_tickTime - _init_time < 0.001 && {ACRE_FAST_HASH_GC_ORPHAN_CHECK_INDEX < (count ACRE_FAST_HASH_CREATED_HASHES)}} do { + private _check = ACRE_FAST_HASH_CREATED_HASHES select ACRE_FAST_HASH_GC_ORPHAN_CHECK_INDEX; + ACRE_FAST_HASH_GC_ORPHAN_CHECK_INDEX = ACRE_FAST_HASH_GC_ORPHAN_CHECK_INDEX + 1; + if (!(_check in ACRE_FAST_HASH_GC_CHECK_OBJECTS)) then { + ACRE_FAST_HASH_TO_DELETE pushBack _check; + }; + }; + } else { + ACRE_FAST_HASH_VAR_STATE = (allVariables missionNamespace); + ACRE_FAST_HASH_CREATED_HASHES = ACRE_FAST_HASH_GC_CHECK_OBJECTS; + ACRE_FAST_HASH_GC_CHECK_OBJECTS = []; + ACRE_FAST_HASH_GC_FOUND_ARRAYS = []; + ACRE_FAST_HASH_VAR_LENGTH = count ACRE_FAST_HASH_VAR_STATE; + ACRE_FAST_HASH_GC_INDEX = 0; + ACRE_FAST_HASH_CREATED_HASHES append ACRE_FAST_HASH_CREATED_HASHES_NEW; + ACRE_FAST_HASH_CREATED_HASHES_NEW = []; + ACRE_FAST_HASH_GC_FOUND_OBJECTS = []; + ACRE_FAST_HASH_GC_ORPHAN_CHECK_INDEX = 0; + }; +}; diff --git a/addons/main/fnc_hashMonitor.sqf b/addons/main/fnc_hashMonitor.sqf new file mode 100644 index 000000000..b3b39bfca --- /dev/null +++ b/addons/main/fnc_hashMonitor.sqf @@ -0,0 +1,30 @@ +#include "script_component.hpp" +/* + * Author: ACRE2Team + * Handles deletion and creation of ACRE2 hashes. + * + * Arguments: + * None + * + * Return Value: + * None + * + * Example: + * [] call acre_main_fnc_hashMonitor + * + * Public: No + */ + +if (count ACRE_FAST_HASH_TO_DELETE > 0) then { + private _init_time = diag_tickTime; + while {(diag_tickTime - _init_time) * 1000 < 2.0 && {ACRE_FAST_HASH_TO_DELETE isNotEqualTo []}} do { + deleteLocation (ACRE_FAST_HASH_TO_DELETE deleteAt 0); + ACRE_FAST_HASH_POOL pushBack HASH_CREATE_NAMESPACE; + }; +}; + +if (count ACRE_FAST_HASH_POOL <= (count ACRE_FAST_HASH_CREATED_HASHES) * 0.1) then { + for "_i" from 1 to 10 do { + ACRE_FAST_HASH_POOL pushBack HASH_CREATE_NAMESPACE; + }; +}; diff --git a/addons/main/script_macros.hpp b/addons/main/script_macros.hpp index 5dd251232..069f0ab25 100644 --- a/addons/main/script_macros.hpp +++ b/addons/main/script_macros.hpp @@ -110,17 +110,17 @@ SQF equivalent of extensions/src/ACRE2Shared/Types.h #define GET_TS3ID(object) (object call { private _ret = (_this getVariable [QGVAR(ts3id), -1]); if (_ret == -1) then { WARNING_1("%1 has no TS3 ID",_this); }; _ret }) -#define IS_HASH(hash) (hash isEqualType createHashMap) - -#define HASH_CREATE_NAMESPACE (createHashMap) -#define HASH_CREATE (createHashMap) -#define HASH_DELETE(hash) (hash = nil) -#define HASH_HASKEY(hash, key) (key in hash) -#define HASH_SET(hash, key, val) (hash set [key,val]) -#define HASH_GET(hash, key) (hash get key) -#define HASH_REM(hash, key) (hash deleteAt key) -#define HASH_COPY(hash) (+hash) -#define HASH_KEYS(hash) (keys hash) +#define IS_HASH(hash) (hash isEqualType locationNull && {(type hash) isEqualTo "ACRE_FastHashNamespaceDummy"}) + +#define HASH_CREATE_NAMESPACE (createLocation ["ACRE_FastHashNamespaceDummy", [-1000, -1000, 0], 0, 0]) +#define HASH_CREATE (call EFUNC(main,fastHashCreate)) +#define HASH_DELETE(hash) (ACRE_FAST_HASH_TO_DELETE pushBack hash) +#define HASH_HASKEY(hash, key) (!(isNil {hash getVariable key})) +#define HASH_SET(hash, key, val) (hash setVariable [key,val]) +#define HASH_GET(hash, key) (hash getVariable key) +#define HASH_REM(hash, key) (hash setVariable [key,nil]) +#define HASH_COPY(hash) (hash call EFUNC(main,fastHashCopy)) +#define HASH_KEYS(hash) (hash call EFUNC(main,fastHashKeys)) #define HASHLIST_CREATELIST(keys) [] #define HASHLIST_CREATEHASH(hashList) HASH_CREATE