Description
This article outlines the optimization settings available for the CodeGen addon. The classes generated by the CodeGen addon and the MonoBehaviour code generator can achieve better performance with these settings. If these classes serve as your primary API for database access, we highly recommend using all optimization parameters. To ensure that the generated MonoBehaviour classes also benefit from these parameters, enable them and toggle the "Use CodeGen addon generated classes" option in the MonoBehaviour generator settings, then re-generate the classes.
Below is an example of the settings (optimization-related settings are highlighted in red, please ignore all other settings).
Generate metas
Additional classes for tables (metas) are generated, eliminating the need for down-casting rows (entities). This parameter is necessary for some other settings.
Storage
The most important setting. It defines where cell values are stored and improve getters and raw setters performance
Default
- cell values for most fields are stored in an array within the field class. (slower)GeneratedClasses
- cell values for some fields are stored in generated class (faster). Additional classes for fields are generated
To ensure that the generated classes for the database fields are in use, navigate to 'Configuration -> $TableName' and check the 'Type & Class' column. An asterisk will appear next to the field type if the generated classes are being used. Some fields are not supported (as of v. 1.9.2): nested, assets, calculated/programmable, scene object references, hashtable.
Cache relation values
Caching relation field values in generated classes can significantly enhance the performance of getter methods. This setting is ideal for scenarios with frequent reads and infrequent writes. The memory cost is as follows:
- Single row relations: is 9 bytes per row (1 byte for the dirty flag + pointer to the row)
- Multiple rows relation: depends on value size (1 byte for the dirty flag + an additional list for the value)
Important note on caching list (relationMultiple) values:
- Values can not be null anymore, if no value exist, an empty list will be returned
- There is no need to use FillXXX methods to get rid of GC overhead (except for values in the reverse direction)
- When you read the value, a List object is returned, which stores the cached value.
If you modify this list, you must pass it back to the setter, otherwise data integrity may be compromised.
var list = entity.someList; list.Add(SomeTable.GetEntity(0)); // <- this call modifies the cached value, not the database value entity.someList = list; // <- this call is very important
Other relations optimizations
A new setting, Ignore list order
. has been added to relational fields. Use it for:
- Nested fields, when the order of nested rows is not important.
- Other relational fields, when the order of rows returned while traversing the relation in reverse is not important.
Runtime initialization
This parameter defines how the fields of generated classes are initialized:
On Demand
- (slow) the C# fields for database tables and fields are initialized on demandManual
- (fast) you must manually initialize the runtime by calling theBGCodeGenRuntimeInitializer.InitializeCodeGenRuntime
method (fast). You need to callBGCodeGenRuntimeInitializer.InitializeCodeGenRuntime();
method before accessing database and after loading the game with the SaveLoad addon if "Reload database" parameter is used. Failing to call this method will result in a NullReferenceException when accessing the database via generated classes.On Database Load
- (fast) the runtime is automatically initialized after the database is loaded. This means the database must be loaded before you can access it via generated classes. You can load the database using the following code:if (!BGRepo.DefaultRepoLoaded) BGRepo.Load();
Raw setters
Raw setters are additional properties that allow you to set values while bypassing events. Regular setters are slower due to event dispatching. If you don't need events to be triggered, using raw setters can enhance performance. Raw setters are generated for certain fields (such as primitive fields) only if those fields are not included in keys or indexes.
Garbage Collection overhead
Some methods may allocate heap memory, leading to garbage collection (GC) overhead. If these methods are called during infrequent events, like scene loading, the impact is negligible. However, if called in a tight loop, such as in the Update or FixedUpdate methods, it's advisable to avoid heap memory allocation. Typically, using a reusable list addresses this issue. If you use Unity profiler while running the game inside Unity Editor, close BGDatabase window to get accurate results.
# | Method | Solution |
---|---|---|
1 | FindEntities method | Use corresponding FillXXX method and pass a reusable list to this method.
Code example
|
2 | nested field getter | The same as #1 (FillXXX method)
Code example
|
3 | The getters for relations with List value (relationMultiple / viewRelationMultiple) |
Code example for option #1
|
4 | The getters for List values while traversing relations in reverse direction | The same as #1 (FillXXX method)
Code example for option #1
|
Important note: You can add reusable lists directly to the generated class and include your own getters and setters that do not produce garbage. For example
Adding your own members to generated class
using System.Collections.Generic;
//BansheeGz.BGDatabase.Example is the namespace of generated class
namespace BansheeGz.BGDatabase.Example
{
//BGE_AllFields is the name of generated class
partial class BGE_AllFields
{
//reusable list for enumList field
private static readonly List<BGUtilForTest.TestEnumInt> reusableEnumList = new();
//property without GC
public List<BGUtilForTest.TestEnumInt> enumListNoGC
{
get
{
Fillf_enumList(reusableEnumList);
return reusableEnumList;
}
}
}
}
Keeping a list static is much more memory efficient, but you need to remember that it can only contain the result for the last row for which the property is called. You can make it non-static if you prefer.
General performance tips
- Events degrade performance- database events can have negative impact on setter methods performance. If you can do without events, don't use them.
- If you need to delete multiple entities, use BGMetaEntity.DeleteEntities method instead of BGEntity.Delete. Version 1.9.2 delivers a significant performance boost for this method.
"Slow" fields to be aware of
Field | Description |
---|---|
asset fields | Reading a value requires loading an asset, which can be slow on the first attempt. Use Addressables with asynchronous loading for these fields. |
calculated fields | Try to not read the values of such fields in hot loops. Replace them with programmable fields or custom C# properties |
relation fields | These fields are the most problematic. Caching their values can address slow getters in "frequent reads and infrequent writes" scenarios. However, setters may still be relatively slow, especially in certain situations. Be cautious when using relation field setters in hot loops. |
Misc
- Check for deletion- If you toggle on this parameter, additional checks for entity deletion is added to every getter/setter methods. Without these checks, accessing or modifying field values of deleted entity are not detected. If you do not use database in read-only mode, we recommend to use this parameter during development to detect accidental access to deleted entity.
Conclusion
Test your game's performance on the minimum required hardware before release. Contact us if you experience performance issues with our asset.