Object initialization in BYOND depends upon a series of reference numbers. When a reference number is freed, it may not be contiguous. Therefore, in order to keep track of these reference IDs that have been freed, ether a search for an available reference must be performed, or a collection of discarded references must be maintained. (more likely).
The result of this, is that creating games with dynamic maps, or repeated creation and deletion of objects results in progressively longer object initialization times. This would single-handedly account for Space Station 13's progressively worsening performance, and limits the viability of things like particle systems.
The testing environment:
The following test environment will prove without a doubt that the creation of a large number of objects, and their subsequent deletion will result in permanently impacted performance.
The following test environment will also prove without a doubt that the issue is not due alone to the number of objects in existence, but rather the number of dead references. Ironically, performance is less hindered by having a lot of objects in your world, than by removing a lot of objects from your world. By a more than significant margin.
var
list/prefill[1000000]
objscreated = 0
runtime = 0
lastobj = null
avobjs = 0
mob
Stat()
stat("count:",num2text(objscreated,10))
stat("runtime:","[runtime / 10] seconds")
stat("lastobj:",lastobj)
stat("average:","[avobjs] per second")
client
verb
pre_fill()
world << "Object buffer being filled..."
sleep(world.tick_lag)
var/obj/o
var/start = world.timeofday
objscreated = 0
runtime = 0
lastobj = null
for(var/count=1;count<=1000000;count++)
o = new()
global.prefill[count] = o
global.lastobj = "\ref[o]"
global.runtime = world.timeofday - start
global.objscreated++
if(global.runtime>0)
avobjs = objscreated / (runtime / 10)
world << "Object buffer filled."
clear_fill()
world << "Object buffer will be cleared..."
sleep(world.tick_lag)
var/l[1000000]
global.prefill = l
world << "Object buffer cleared."
massobj()
world << "Object mass reference/dereference begin..."
sleep(world.tick_lag)
var/obj/o
var/start = world.timeofday
objscreated = 0
runtime = 0
lastobj = null
for(var/count=0;count<1000000;count++)
o = new()
global.lastobj = "\ref[o]"
global.runtime = world.timeofday - start
global.objscreated++
if(global.runtime>0)
avobjs = objscreated / (runtime / 10)
world << "Object mass reference/dereference complete."
massmob()
world << "Mob mass reference/dereference begin..."
sleep(world.tick_lag)
var/mob/o
var/start = world.timeofday
objscreated = 0
runtime = 0
lastobj = null
for(var/count=0;count<1000000;count++)
o = new()
global.lastobj = "\ref[o]"
global.runtime = world.timeofday - start
global.objscreated++
if(global.runtime>0)
avobjs = objscreated / (runtime / 10)
world << "Mob mass reference/dereference complete."
Sequence of verbs:
1) massobj
2+) massobj
last) massmob
You will note that in this testing sequence, 1 million objects are created in sequence, and not allowed to be cleared.
The first massobj test results on my machine in 1 million objects being created in approximately 21 seconds. This is an approximate object creation rate of 47,000 objects per second.
Every subsequent massobj test will result in 1 million objects being created in approximately 780 seconds, despite the fact that none of the objects from the previous test are preserved. You will also notice that the objects do not reclaim recently-dereferenced ids, but instead consume reference ids that were abandoned prior to the current tick. This behavior results in less than 1400 objects being created per second thereafter until the world is rebooted.
You will also see that the first massmob test you run will result in 1 million mobs being created and dereferenced in roughly 21 seconds again, back up to 47,000 objects per second.
However, every subsequent massmob test you run will similarly climb just as the massobj test did.
Interpretation:
This indicates that finding the reference id for the individual object is to blame. Mobs and Objs use separate means of tracking references, and therefore, also demonstrates that there is little effect on initialization of objects relating to the number of objects in the world as a whole. It also indicates that garbage collection itself as a process cannot be the cause of the increased CPU usage.
Beta 506.1243
1) massobj: 1M @ 3.4 seconds, 294,118/sec, lastobj: 0x2000001 (mem stable at 15 megs)
2) massobj: 1M @ 3.4 seconds, 294,118/sec, lastobj: 0x2000000 (mem stable at 15 megs)
3) massmob: 1M @ 3.5 seconds, 285,714/sec, lastobj: 0x3000002 (mem stable at 15 megs)
Test 1 results in expected behavior with added bonus of massive speed improvements on the order of 500-600% faster than before.