mirror of
https://github.com/CHOMPStation2/CHOMPStation2.git
synced 2025-12-09 16:12:17 +00:00
Co-authored-by: Selis <12716288+ItsSelis@users.noreply.github.com> Co-authored-by: Kashargul <144968721+Kashargul@users.noreply.github.com>
200 lines
7.1 KiB
Plaintext
200 lines
7.1 KiB
Plaintext
#define LOOP_STEP_SIZE 25000
|
|
//#define MEM_NO_CHECK_TICK
|
|
#ifdef MEM_NO_CHECK_TICK
|
|
/world
|
|
loop_checks = 0
|
|
#endif
|
|
/proc/cmp_numeric_desc(a,b)
|
|
return b - a
|
|
|
|
/proc/profile_memory()
|
|
if(usr?.client)
|
|
if(tgui_alert(usr,"Running this will likely cause minor lag for around 20 minutes and the server will freeze for a bit at the end", "Profile memory", list("Yes", "No")) != "Yes")
|
|
return
|
|
var/list/types_count = list()
|
|
var/list/mem_count = list()
|
|
var/list/by_variable = list()
|
|
var/list/list_of_lists = list()
|
|
var/list/list_count = list()
|
|
var/list/exclude_vars = list("overlays","underlays","vis_contents","vis_locs","contents","vars","verbs")
|
|
world.log << "Counting memory for atoms"
|
|
var/i = 0
|
|
var/total_time = 0
|
|
rustg_time_reset("fops")
|
|
for(var/datum/thing in world)
|
|
types_count[thing.type]++
|
|
#ifndef MEM_NO_CHECK_TICK
|
|
if(!thing || QDELETED(thing)) continue
|
|
#endif
|
|
if(i%LOOP_STEP_SIZE==0)
|
|
var/ms = rustg_time_milliseconds("fops")
|
|
total_time += ms
|
|
rustg_time_reset("fops")
|
|
world.log << "[i] atoms processed in [total_time/1000] seconds. ([ms]ms for chunk)"
|
|
world.log << "list of lists at [list_of_lists.len] before pruning"
|
|
prune_list(list_of_lists)
|
|
world.log << "list of lists at [list_of_lists.len] after pruning"
|
|
mem_and_lists(thing,list_of_lists,list_count,exclude_vars,mem_count,by_variable)
|
|
i++
|
|
#ifndef MEM_NO_CHECK_TICK
|
|
CHECK_TICK
|
|
#endif
|
|
i = 0
|
|
world.log << "Counting memory for datums"
|
|
for(var/datum/thing)
|
|
types_count[thing.type]++
|
|
#ifndef MEM_NO_CHECK_TICK
|
|
if(!thing || QDELETED(thing)) continue
|
|
#endif
|
|
if(i%LOOP_STEP_SIZE==0)
|
|
var/ms = rustg_time_milliseconds("fops")
|
|
total_time += ms
|
|
rustg_time_reset("fops")
|
|
world.log << "[i] datums processed in [total_time/1000] seconds. ([ms]ms for chunk)"
|
|
world.log << "list of lists at [list_of_lists.len] before pruning"
|
|
prune_list(list_of_lists)
|
|
world.log << "list of lists at [list_of_lists.len] after pruning"
|
|
mem_and_lists(thing,list_of_lists,list_count,exclude_vars,mem_count,by_variable)
|
|
i++
|
|
#ifndef MEM_NO_CHECK_TICK
|
|
CHECK_TICK
|
|
#endif
|
|
var/accounted_for = 0
|
|
for(var/type in mem_count) //Quickly prune anything below 10kb total usage to make sorting the list much faster
|
|
accounted_for += mem_count[type]
|
|
if(mem_count[type] < 10000) mem_count -= type
|
|
world.log << "[display_bytes(accounted_for)] of memory accounted for"
|
|
world.log << "Sorting and exporting data"
|
|
to_chat(world, span_alert("Memory profiler is exporting data. Expect server to freeze for 10-30 seconds."))
|
|
sortTim(types_count, /proc/cmp_numeric_desc, TRUE)
|
|
var/output = ""
|
|
for(var/type in types_count)
|
|
output += "[type] - [types_count[type]]\n"
|
|
rustg_file_write(output, "data/stuff.txt")
|
|
output = ""
|
|
sortTim(mem_count, /proc/cmp_numeric_desc, TRUE)
|
|
for(var/type in mem_count)
|
|
var/mem_per_instance = mem_count[type] / types_count[type]
|
|
output += "[type] - [display_bytes(mem_count[type])] total - [display_bytes(mem_per_instance)] per instance"
|
|
if(type in list_count)
|
|
var/lists_per_instance = list_count[type] / types_count[type]
|
|
output += " - [list_count[type]] lists total - [lists_per_instance] per instance"
|
|
output += "\n"
|
|
var/ms = rustg_time_milliseconds("fops")
|
|
rustg_file_write(output, "data/lists.txt")
|
|
var/list/count_subtypes = list()
|
|
for(var/type in types_count)
|
|
add_types_val(type, count_subtypes, types_count[type])
|
|
var/list/mem_count_subtypes = list()
|
|
for(var/type in mem_count)
|
|
add_types_val(type, mem_count_subtypes, mem_count[type])
|
|
output = ""
|
|
sortTim(mem_count_subtypes, /proc/cmp_numeric_desc, TRUE)
|
|
for(var/type in mem_count_subtypes)
|
|
var/mem_per_instance = mem_count_subtypes[type] / count_subtypes[type]
|
|
output += "[type] - [display_bytes(mem_count_subtypes[type])] total - [display_bytes(mem_per_instance)] per instance"
|
|
output += "\n"
|
|
rustg_file_write(output, "data/lists-subtypes.txt")
|
|
output = ""
|
|
sortTim(by_variable, /proc/cmp_numeric_desc, TRUE)
|
|
for(var/type in by_variable)
|
|
output += "[type] - [display_bytes(by_variable[type])] total"
|
|
output += "\n"
|
|
total_time += ms
|
|
rustg_time_reset("fops")
|
|
output += "\nTotal time: [total_time/1000] seconds"
|
|
rustg_file_write(output, "data/membyvariable.txt")
|
|
|
|
world.log << "Finished in [total_time/1000] seconds"
|
|
|
|
|
|
/proc/prune_list(var/list/list_of_lists)
|
|
if(!list_of_lists.len) return
|
|
for(var/list/L in list_of_lists)
|
|
if(list_of_lists[L] == 1 && refcount(L) < 10) list_of_lists.Remove(list(L))
|
|
|
|
/proc/mem_and_lists(var/datum/thing,var/list/list_of_lists,var/list/list_count,var/list/exclude_vars,var/list/mem_count,var/list/by_variable)
|
|
mem_count[thing.type] += 24
|
|
for(var/variable in thing.vars)
|
|
if(variable == "vars") continue
|
|
if(islist(thing.vars[variable]))
|
|
if(!(variable in exclude_vars) && refcount(thing.vars[variable]) > 1 && (thing.vars[variable] in list_of_lists))
|
|
if(thing.vars[variable] != initial(thing.vars[variable]))
|
|
mem_count[thing.type] += 16
|
|
by_variable[variable] += 16
|
|
list_of_lists[thing.vars[variable]]++
|
|
continue
|
|
if(thing.vars[variable] != initial(thing.vars[variable]))
|
|
if(!(variable in exclude_vars) && refcount(thing.vars[variable]) > 1) list_of_lists[thing.vars[variable]] = 1
|
|
var/mem_size = list_memory_size(thing.vars[variable],list_of_lists)
|
|
mem_count[thing.type] += mem_size
|
|
by_variable[variable] += mem_size
|
|
if(variable in exclude_vars)
|
|
continue
|
|
if(thing.type in list_count) list_count[thing.type]++
|
|
else list_count[thing.type] = 1
|
|
else if(thing.vars[variable] != initial(thing.vars[variable]))
|
|
mem_count[thing.type] += 16
|
|
by_variable[variable] += 16
|
|
|
|
/proc/list_memory_size(list/L,list/list_of_lists,list/recursed_from)
|
|
if((L in recursed_from) || LAZYLEN(recursed_from) > 64 || (LAZYLEN(recursed_from) && (L in list_of_lists))) return 0
|
|
if(LAZYLEN(recursed_from) && refcount(L) > 4)
|
|
if(L in list_of_lists) list_of_lists[L]++
|
|
else list_of_lists[L] = 1
|
|
var/total = 24
|
|
var/associative = is_associative(L)
|
|
var/per_item = associative ? 48 : 8
|
|
if(L.len < 8)
|
|
total += 8 * per_item
|
|
per_item = 0
|
|
for(var/item in L)
|
|
total += per_item
|
|
if(recursed_from) recursed_from[++recursed_from.len] = L
|
|
else recursed_from = list(L)
|
|
if(associative && islist(L[item]))
|
|
total += list_memory_size(L[item],list_of_lists,recursed_from.Copy())
|
|
if(islist(item))
|
|
total += list_memory_size(item,list_of_lists,recursed_from.Copy())
|
|
return total
|
|
|
|
|
|
|
|
//This feels like it shouldn't work but it does
|
|
/proc/is_associative(list/L)
|
|
try
|
|
for(var/item in L)
|
|
if(!isnum(item) && L[item]) return TRUE
|
|
if(!L[item]) return FALSE
|
|
catch
|
|
return FALSE
|
|
|
|
/proc/add_types(var/datum/thing, var/list/L)
|
|
var/type = thing::type
|
|
var/p_type = thing::parent_type
|
|
if(type in L) L[type]++
|
|
else L[type] = 1
|
|
if(p_type) add_types(p_type, L)
|
|
|
|
/proc/add_types_val(var/datum/thing, var/list/L, var/val)
|
|
var/type = thing::type
|
|
var/p_type = thing::parent_type
|
|
L[type] += val
|
|
if(p_type) add_types_val(p_type, L, val)
|
|
/*
|
|
/datum/controller/master/SetRunLevel(new_runlevel)
|
|
if(new_runlevel == RUNLEVEL_GAME)
|
|
spawn(300)
|
|
get_stuff()
|
|
. = ..(new_runlevel)
|
|
*/
|
|
/proc/display_bytes(num_bytes)
|
|
if(num_bytes > 10000000)
|
|
return "[num_bytes/1000000] mb"
|
|
if(num_bytes > 10000)
|
|
return "[num_bytes/1000] kb"
|
|
return "[num_bytes] b"
|
|
|
|
#undef LOOP_STEP_SIZE
|
|
//#undef MEM_NO_CHECK_TICK
|