Of all the awesome features in Unity, I find the ability to script the editor and make it do what you want using almost the same tools and API that the original developers used to make it one of the best features available to developers using Unity. Almost anything you can think of has been done using editor programming, from simple tools to complete editor overhauls, and I too have got my hands dirty with it.

My latest(and biggest) editor project has been making the Visual Item Database, an editor tool that allows developers to easily create, store and manage game items (like swords, armor etc..) in one place and gives the ability to intuitively use items in game. Now I noticed a lot of developers faced problems with such tools when item counts grew beyond a couple of thousands because of lag, instability or simply not being designed to be practical with that many items, so I set out to create an item database that can hold a large number of items without a problem. But naturally, I hit a wall, and fast!

Now as you might expect I was making use of serialized object/property, I mean who wouldn’t, they are generic, they handle undo/redo automatically and they allow you to easily write general code. In short, they are awesome, that is until you start caring about performance.

At only 500 items the tool was practically unusable with extreme lag, I was shocked, because I didn’t expect problems at such a low item count. It was time to investigate!

At 500 items the editor window was in a constant lag state, so even if you want to just scroll through your items, you will barely do so. So I decided the first step was to fire the profiler. As you might have expected something is taking a lot of processing time, 1298ms to be exact, and most of it seems to be coming from this

This much time with it simply being open. I decided to take a look at my list iterator method that handles showing items to see what might be causing this. The only thing I found that was doing any work at all was this line of code:

1//I'm accessing an element of a list that is contained in a serialized property
2itemLists[itemToShow.listIndex].GetArrayElementAtIndex(elementIndex);

So I replaced the above line with this:

 1ItemBase GetItemAtIndex(int index)
 2{
 3    if (itemToShow.listIndex == 0)
 4        return database.genericItems[index];
 5    else if (itemToShow.listIndex == 1)
 6        return database.meleeWeapons[index];
 7    else if (itemToShow.listIndex == 2)
 8        return database.rangedWeapons[index];
 9    else if (itemToShow.listIndex == 3)
10        return database.armor[index];
11    else if (itemToShow.listIndex == 4)
12        return database.consumables[index];
13
14    return null;
15}

and to my surprise, there was a jump in performance and it was huge. By removing just one line I went from unusable to a very usable 86 ms(it actually is very smooth in practice), as you can see here:

For performance this was a decent first step, but in general it was very worrying because other things like reordering, duplicating and deleting items(among other things) all relied on nice methods supplied by serialized property. I needed to do more tests, so I decided to start by seeing what the profiler says when I duplicate/delete an item, and I’m glad I did.

Removing an item

1
list.DeleteArrayElementAtIndex(index);    //'list' is a serialized property

Duplicating an item

1
list.InsertArrayElementAtIndex(index);    //'list' is a serialized property

With a database containing 1496 items, removing an item causes a lag spike(one frame after this picture), and the profiler shows the following:

‘ItemBase..ctor()’ called 1495 times, this is not good. ‘ItemBase’ is the base class for my item scripts, ‘.ctor()’ is basically the constructor, so the constructor was called 1495 times, which is the total item count after removing one item. So what does all of this mean?

Well it appears that what serialized property is doing behind the scenes is making a new list, filling the new list with the wanted amount of items (hence 1495 constructor calls) and then copying the data over from the old list, except for that one item we ‘deleted’. The same happens when duplicating items. This might not seem too bad now because the numbers aren’t big and the spike is not that bad, but when testing with bigger databases and lists, the entire editor stalls for a few seconds every time you duplicate/delete an item, which is something you never want.(well unless you are trying to totally destroy someones day, in which case it is rather effective).

Just like before, the solution is to write more code to do things manually, which while is more work for me it gives a significant boost in performance, in fact the constructor calls are completely eliminated and duplicating/removing items even in large databases no longer causes any problems.

New code for removing items

 1if (itemToShow.listIndex == 0)
 2    database.genericItems.RemoveAt(index);
 3else if (itemToShow.listIndex == 1)
 4    database.meleeWeapons.RemoveAt(index);
 5else if (itemToShow.listIndex == 2)
 6    database.rangedWeapons.RemoveAt(index);
 7else if (itemToShow.listIndex == 3)
 8    database.armor.RemoveAt(index);
 9else if (itemToShow.listIndex == 4)
10    database.consumables.RemoveAt(index);

And code for duplicating now relies on using ‘List.Insert()’ like so:

1database.genericItems.Insert(index, itemCont.item); //Insert duplicate item in the wanted place

Though surprisingly (and in contrast to my expectations), the methods for moving array elements cause no trouble whatsoever.

Moving array elements

1//Moves an item one slot up
2itemLists[itemToShow.listIndex].MoveArrayElement(itemToShow.itemToShowIndex, itemToShow.itemToShowIndex - 1);

By removing these three main lines of code(along with other improvements of course) I was able go from an editor tool that stops functioning at about 500 items, to one that can handle over 10,000 items! This is over 20 times the original performance!

So does this mean we stop using serialized objects/properties? Are they evil?

No, and in fact this tool would not have been possible without them(or would have been quite difficult to do) and I use them in a number of key places throughout my code. But this does mean that we should be careful when dealing with them, especially when it comes to serialized properties of arrays, lists and the like because their implementation is less than optimal, even in simple things like ‘.arraySize’, which is slower than accessing the size of a list directly. This general inefficiency is not entirely surprising though, since they have to be so generic, but I didn’t expect they would be so slow.

In conclusion, I would recommend to generally steer clear of serialized property methods when dealing with arrays, and if you are going above a couple hundred elements in your array, please, do not use ‘GetArrayElementAtIndex’, it is very, very slow. ‘DeleteArrayElementAtIndex’ and ‘InsertArrayElementAtIndex’ are not as bad, but I wouldn’t use them if you are going above 1-2 thousand array elements.

To summarize:

  • Serialized property is good, but highly inefficient when it comes to dealing with collections
  • ‘GetArrayElementAtIndex’ is very slow and inefficient
  • ‘DeleteArrayElementAtIndex’ and ‘InsertArrayElementAtIndex’ are not as bad, but are still inefficient when dealing with large collections
  • Similar serialized object/property methods are relatively slow and should be avoided in high performance pieces of code

If you would like to learn more about this Unity tool, please see here.

Happy editor scripting!