That being said, this is one I'm actually really excited to write. Why? Because Lummox, our glorious dictator has finally decided to squash the need for me to write this installment with a new feature in the much hyped BYOND 511.
What are appearances?
If you've been around a while, you might think that appearances are a new thing to BYOND since almost exactly a year ago.
They aren't though. Appearances have been with us since the beginning. Very few people have ever really figured out what they are and why they do what they do. BYOND is a server-directed engine. This means that the client program has zero actual information about what's going on in a particular game that the server doesn't tell it about.
Now, this is a stark contrast from most game development platforms, where the client makes world pretty and drives the majority of what happens, while the server simply maintains the total state of the simulation without bothering itself with how things actually look.
When you change visual data on an atom, the server actually doesn't like to send the data that you have changed to any watching clients. The server attempts to reduce the total volume of information stored in an appearance to a simple numeric ID. In order for this ID to be of any value, the server has to figure out when the visual properties of one object to another are identical. That way, two objects that have been made to look identical will actually use the same internal appearance object in memory despite the fact that these two objects may not even know about one another at all.
The client is informed about what appearances themselves look like, and which appearances have been gotten rid of or have been newly created within the area of the gameworld it actually is concerned with.
This allows you to make your games pretty without having to worry about resource management for the most part.
The problem
This also has some consequences. It means that appearances are immutable. You can't change an appearance directly because multiple objects may depend on that appearance.
This means that every time you change an appearance-related value, you are actually telling the engine to search every appearance currently in existence to find a match, or if none exists, to create one.
Obviously, you can see the problem here. Big games can have LOTS of appearances in play. This means that what you would think of as some simple code can actually be much less than simple:
icon = 'herp.dmi'
icon_state = "derp"
Internally, this code is going to look something like this:
new_appearance.icon = 'herp.dmi'
var/appearance/match
for(var/appearance/a in appearances)
if(a==new_appearance)
match = a
break
if(!match)
appearance = new_appearance
new_appearance.icon_state = "derp"
for(var/appearance/a in appearances)
if(a==new_appearance)
match = a
break
if(!match)
appearance = new_appearance
If you have any programming experience at all, you'll realize that this is not good-looking, high performance code. Luckily, the engine does do some optimization for us and avoids appearance lookups if an assignment wouldn't actually change the appearance, and also possibly if the appearance only has one reference (I'm not sure about this last part, but it seems to be true.)
How to mitigate this lookup:
This section is reserved for after the release of 511's new appearance mutation feature. This new feature will let us reduce the number of searches on appearance change to exactly 1 no matter how many appearance related variables we change in a row so long as we adopt a new pattern in our code.
Overlays and Underlays
Overlays and underlays are an area where this can currently be mitigated quite a lot. Overlays and Underlays lists are actually internally not lists, but id arrays. Overlays are a list of appearance ids. If you make a change to an atom's overlays/underlays list, every overlay/underlay list in the world is checked for consistency in the same manner as appearances. This reduces the amount of data that BYOND needs to store in active memory a lot, but the sacrifice is CPU.
Recently, while working on Epoch, I spent a lot of time chasing down some inefficiency that was causing the game to take 30 or more seconds to load even on high end machines. The cause was not using more efficient means of changing overlays and underlays.
Every time you add or remove something from id arrays, a similar search happens. So the trick to working with overlays efficiently to avoid more than a single search is to copy the list if you need to work with it multiple times and then make your changes.
//4 churns
overlays += 'herp.dmi'
overlays += 'derp.dmi'
overlays += 'herpy.dmi'
overlays += 'derpy.dmi'
//1 churn
overlays = overlays.Copy()+'herp.dmi'+'derp.dmi'+'herpy.dmi'+'derpy.dmi'
//4 churns
overlays += 'herp.dmi'
overlays -= 'derp.dmi'
overlays -= 'herpy.dmi'
overlays += 'derpy.dmi'
//1 churn
overlays = overlays.Copy()+'herp.dmi'-'derp.dmi'-'herpy.dmi'+'derpy.dmi'
Also, a few common patterns that you can avoid:
//lots of churn
turf
New()
overlays += 'someoverlay.dmi'
//no churn
turf
overlays = list('someoverlay.dmi')
Overlay churn was costing Epoch almost 30 seconds on launch. I'm sure you'll find minimizing churn pretty useful yourself once you understand WHY something as simple as adding things to the overlays list can take a while.
<--- has some programming experience and thinks the code looks fine :P. Can you explain why its not good looking/ high performance for me please :D? Or link me to where you already explained :D?