mirror of
https://github.com/Bubberstation/Bubberstation.git
synced 2025-12-09 07:46:20 +00:00
* Refactors fancy type generation (#81259) ## About The Pull Request [Refactors fancy type generation](3f218ac7b7) Ok so we have this proc that generates concatenated names for types so admins have a nice list to sort through. The trouble is this is done by, for each type, iterating all possible replacements, and seeing which ones apply (with expensive string operations) A clean run of this applied to all datums takes about 3.5 seconds on my pc. This sucks. Ok so can we do better. Well, yes, pretty easily. Rather then, for each potential type, iterating all the options, let's build a zebra typecache (a lookup list of type -> string to use), and use that. Then we can use a list of replacement -> the bit to tear out to figure out what to remove. This works quite well. It does mean that we're doing it based off the type tree and not type paths, so if we didn't have a replacement for like, mob, it'd look weird, but we don't have cases like that so it's fine. Or well we sorta did, didn't have anything for atom movables or areas, but I fixed that so sall good. Anyway, we only need to do this work once. It takes about 0.3 seconds on my machine, so we can cache it. Just this on its own would technically slow init, since we have a some code that's running this proc off static, but we can just not, that's fine (technically saves init time too since we don't have to burn 0.1 seconds on it anymore). This brings the cost of generating this list for all datums from 3 seconds to 0.16, assuming we have the static pre generated. We could in theory pre-generate just like, all the strings? But I don't think the cached cost is high enough for that to be a real problem. IDK open to other thoughts Oh also I had to reorder the strings in that list, cause zebra_typecacheof has reverse priority. s life [Updates stat tracking macro to work at world start](1fbfb701a1) It for some reason doesn't actually get anything this early, but now at least the logging would in theory function ## Why It's Good For The Game Better response times for admins, faster code, more better * Refactors fancy type generation --------- Co-authored-by: LemonInTheDark <58055496+LemonInTheDark@users.noreply.github.com>
68 lines
2.7 KiB
Plaintext
68 lines
2.7 KiB
Plaintext
#define STAT_ENTRY_TIME 1
|
|
#define STAT_ENTRY_COUNT 2
|
|
#define STAT_ENTRY_LENGTH 2
|
|
|
|
|
|
#define STAT_START_STOPWATCH var/STAT_STOP_WATCH = TICK_USAGE
|
|
#define STAT_STOP_STOPWATCH var/STAT_TIME = TICK_USAGE_TO_MS(STAT_STOP_WATCH)
|
|
#define STAT_LOG_ENTRY(entrylist, entryname) \
|
|
var/list/STAT_ENTRY = entrylist[entryname] || (entrylist[entryname] = new /list(STAT_ENTRY_LENGTH));\
|
|
STAT_ENTRY[STAT_ENTRY_TIME] += STAT_TIME;\
|
|
STAT_ENTRY[STAT_ENTRY_COUNT] += 1;
|
|
|
|
// Cost tracking macros, to be used in one proc. If you're using this raw you'll want to use global lists
|
|
// If you don't you'll need another way of reading it
|
|
#define INIT_COST(costs, counting) \
|
|
var/list/_costs = costs; \
|
|
var/list/_counting = counting; \
|
|
var/_usage = TICK_USAGE;
|
|
|
|
// STATIC cost tracking macro. Uses static lists instead of the normal global ones
|
|
// Good for debug stuff, and for running before globals init
|
|
#define INIT_COST_STATIC(...) \
|
|
var/static/list/hidden_static_list_for_fun1 = list(); \
|
|
var/static/list/hidden_static_list_for_fun2 = list(); \
|
|
INIT_COST(hidden_static_list_for_fun1, hidden_static_list_for_fun2)
|
|
|
|
// Cost tracking macro for global lists, prevents erroring if GLOB has not yet been initialized
|
|
#define INIT_COST_GLOBAL(costs, counting) \
|
|
INIT_COST_STATIC() \
|
|
if(GLOB){\
|
|
costs = hidden_static_list_for_fun1; \
|
|
counting = hidden_static_list_for_fun2 ; \
|
|
} \
|
|
_usage = TICK_USAGE;
|
|
|
|
|
|
#define SET_COST(category) \
|
|
do { \
|
|
var/_cost = TICK_USAGE; \
|
|
_costs[category] += TICK_DELTA_TO_MS(_cost - _usage);\
|
|
_counting[category] += 1; \
|
|
} while(FALSE); \
|
|
_usage = TICK_USAGE;
|
|
|
|
#define SET_COST_LINE(...) SET_COST("[__LINE__]")
|
|
|
|
/// A quick helper for running the code as a statement and profiling its cost.
|
|
/// For example, `SET_COST_STMT(var/x = do_work())`
|
|
#define SET_COST_STMT(code...) ##code; SET_COST("[__LINE__] - [#code]")
|
|
|
|
#define EXPORT_STATS_TO_JSON_LATER(filename, costs, counts) EXPORT_STATS_TO_FILE_LATER(filename, costs, counts, stat_tracking_export_to_json_later)
|
|
#define EXPORT_STATS_TO_CSV_LATER(filename, costs, counts) EXPORT_STATS_TO_FILE_LATER(filename, costs, counts, stat_tracking_export_to_csv_later)
|
|
|
|
#define EXPORT_STATS_TO_FILE_LATER(filename, costs, counts, proc) \
|
|
do { \
|
|
var/static/last_export = 0; \
|
|
/* Need to always run if we haven't yet, since this code can be placed ANYWHERE */ \
|
|
if (world.time - last_export > 1.1 SECONDS || (last_export == 0)) { \
|
|
last_export = world.time; \
|
|
/* spawn() is used here because this is often used to track init times, where timers act oddly. */ \
|
|
/* I was making timers and even after init times were complete, the timers didn't run :shrug: */ \
|
|
spawn (1 SECONDS) { \
|
|
##proc(filename, costs, counts); \
|
|
} \
|
|
} \
|
|
} while (FALSE); \
|
|
_usage = TICK_USAGE;
|