NodeCanvas Forums › General Discussion › Optimizations
I’ve made a few code changes / optimizations I’d like to share.
1) I noticed that the GetData() method in the Blackboard.cs was expensive. It turns out when you search it does a linear search based on name matching. If you have quite a few variables this can be expensive. I ended up adding a dicitonary for lookups and instead search my dictionary for the key. There is a little bookkeeping involved in functions AddData(), DeleteData(), Load(), OnValidate(), ShowVariablesGUI() functions to keep the dictionary and the “variables” list in sync. This may not be the BEST fix as I’m side-stepping the NCIsAssignableFrom() code.
1 2 3 4 5 6 7 8 9 10 11 12 13 |
public VariableData GetData(string dataName, Type ofType){ VariableData lastAssignable = null; foreach (VariableData data in variables){ if (data.dataName == dataName){ if ( ofType == null || data.varType == ofType || ofType.NCIsAssignableFrom(data.varType) /* || data.varType.NCIsAssignableFrom(ofType) */ ) return data; if (data.varType.NCIsAssignableFrom(ofType)) lastAssignable = data; } } |
Changed to:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
private Dictionary<string, VariableData> _variableLookup = new Dictionary<string,VariableData>(); public VariableData GetData(string dataName, Type ofType){ if (_variableLookup.ContainsKey(dataName)) { return _variableLookup[dataName]; } else { return null; } } |
2) In BBVariables.cs the Read<T> function does something a bit strange when comparing a boolean blackboard variable and ends up doing some calls to bool.ToString(). I found that fetching and storing the objectValue from the dataRef once eliminated the problem.
1 2 3 4 5 6 7 8 |
if (dataRef != null) { if (dataRef.objectValue == null || dataRef.objectValue.Equals(null)) return default(T); return (T)dataRef.objectValue; } |
Changed to:
1 2 3 4 5 6 7 8 9 |
if (dataRef != null) { var objValue = dataRef.objectValue; if (objValue == null || objValue.Equals(null)) return default(T); return (T)objValue; } |
3) In BBVariables.cs the Read<T> function again, I noticed a data fetch twice:
1 2 3 4 5 6 7 |
if (bb != null) { dataRef = bb.GetData(dataName, typeof(T)); return bb.GetDataValue<T>(dataName); } |
Changed to
1 2 3 4 5 6 7 8 |
if (bb != null) { dataRef = bb.GetData(dataName, typeof(T)); if (dataRef) return (T) dataRef.objectValue; } |
4) The largest HITCH in the system was creating a variable in the blackboard. Anytime you add a variable via “AddData()” in Blackboard.cs it walks data types using reflection via GetTypeDataRelation(). I didn’t think it was necessary to regenerate the GetTypeDataRelation() results more that ONCE – in OUR game. So I just cached it as a static dictionary. This was perhaps the biggest win against hitching. I just check to see if the cached version is null set it. Then changed the foreach enumerators to use the cached version.
1 2 3 4 5 6 7 8 9 10 |
private VariableData AddData(string name, Type type) { var typePairs = GetTypeDataRelation(); foreach (KeyValuePair<Type, Type> pair in typePairs) { ... } |
Changed to :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
private static Dictionary<Type, Type> _typeDataRelation; private VariableData AddData(string name, Type type) { if ( _typeDataRelation == null ) _typeDataRelation = GetTypeDataRelation(); foreach (KeyValuePair<Type, Type> pair in _typeDataRelation) { ... } } |
5) I wanted a way of pre-allocating all of our blackboard variables. I created an then exposed a function in Blackboard.cs for this purpose: (Here I’m using the variable map variable from #1).
1 2 3 4 5 6 7 8 9 |
public void InitData(string name, Type type) { if (_variableLookup.ContainsKey(name)) return; else AddData(name, type); } |
Now I can use the following to get around in-game variable creation.
1 2 3 |
InitData( "key", typeof(bool)); |
I can provide more details to these optimizations if needed. These are quick optimizations that I found by using the “deep” profile in Unity Pro. I have 120+ agents with multiple 10+ smaller subtrees. I was looking at both GC allocs and general update times.
I’m hoping that some of these optimizations can help others. I’d appreciate the author review these changes to see if they should be included in some form. I understand too that these may have some unintentional side-effects that I’m not fully aware of *yet*. However, they have helped *tremendously* and work well with how we are using the product.
I really like this product and the ability to make changes.
Hey,
Thanks a bunch for your optimization suggestions.
I will definetely look those after and put to use one way or another for the next update 🙂
Cheers!
Join us on Discord: https://discord.gg/97q2Rjh