From 6c41aba37f7976cdd1f8b6ab94c0707afb91e4eb Mon Sep 17 00:00:00 2001 From: Noel Stephens Date: Thu, 13 Mar 2025 22:02:07 -0500 Subject: [PATCH 1/2] perf: reduce networkbehaviourilpp type resolution time (#3227) User submitted PR: #3224 ## Measurement With `minimalproject`, I used the following code for measurement. ```diff diff --git a/com.unity.netcode.gameobjects/Editor/CodeGen/NetworkBehaviourILPP.cs b/com.unity.netcode.gameobjects/Editor/CodeGen/NetworkBehaviourILPP.cs index 7e95fea9..39b4f991 100644 --- a/com.unity.netcode.gameobjects/Editor/CodeGen/NetworkBehaviourILPP.cs +++ b/com.unity.netcode.gameobjects/Editor/CodeGen/NetworkBehaviourILPP.cs @@ -77,6 +77,8 @@ namespace Unity.Netcode.Editor.CodeGen m_Diagnostics.Clear(); + var sw = System.Diagnostics.Stopwatch.StartNew(); + // read var assemblyDefinition = CodeGenHelpers.AssemblyDefinitionFor(compiledAssembly, out m_AssemblyResolver); if (assemblyDefinition == null) @@ -171,6 +173,8 @@ namespace Unity.Netcode.Editor.CodeGen assemblyDefinition.Write(pe, writerParameters); + m_Diagnostics.AddWarning($"ILPP has completed for {compiledAssembly.Name} in {sw.ElapsedMilliseconds} ms"); + return new ILPostProcessResult(new InMemoryAssembly(pe.ToArray(), pdb.ToArray()), m_Diagnostics); } ``` ### Before ``` (0,0): warning - ILPP has completed for Unity.Netcode.Editor in 531 ms (0,0): warning - ILPP has completed for Unity.Netcode.EditorTests in 604 ms (0,0): warning - ILPP has completed for Unity.Netcode.Components in 363 ms (0,0): warning - ILPP has completed for Unity.Netcode.TestHelpers.Runtime in 330 ms (0,0): warning - ILPP has completed for Unity.Netcode.RuntimeTests in 735 ms ``` ### After ``` (0,0): warning - ILPP has completed for Unity.Netcode.Editor in 79 ms (0,0): warning - ILPP has completed for Unity.Netcode.EditorTests in 128 ms (0,0): warning - ILPP has completed for Unity.Netcode.Components in 146 ms (0,0): warning - ILPP has completed for Unity.Netcode.TestHelpers.Runtime in 117 ms (0,0): warning - ILPP has completed for Unity.Netcode.RuntimeTests in 537 ms ``` ## Changelog - Changed: Improved performance of NetworkBehaviour ILPostProcessor by omitting unnecessary type and assembly resolutions. ## Testing and Documentation - No tests have been added. - No documentation changes or additions were necessary. --------- Co-authored-by: ruccho --- .../Editor/CodeGen/NetworkBehaviourILPP.cs | 84 +++++++++++++------ 1 file changed, 60 insertions(+), 24 deletions(-) diff --git a/com.unity.netcode.gameobjects/Editor/CodeGen/NetworkBehaviourILPP.cs b/com.unity.netcode.gameobjects/Editor/CodeGen/NetworkBehaviourILPP.cs index 620140ea02..04a72d7c9e 100644 --- a/com.unity.netcode.gameobjects/Editor/CodeGen/NetworkBehaviourILPP.cs +++ b/com.unity.netcode.gameobjects/Editor/CodeGen/NetworkBehaviourILPP.cs @@ -985,18 +985,60 @@ private bool ImportReferences(ModuleDefinition moduleDefinition, string[] assemb foreach (var method in type.Methods) { + // to reduce unnecessary type resolution, we first check if it can be a serialization extension without type resolution + if (!method.IsStatic) { continue; } + if (method.Name is not (k_WriteValueMethodName or k_ReadValueMethodName)) + { + continue; + } + + var mayHaveExtensionAttr = false; + + foreach (var attr in method.CustomAttributes) + { + if (attr.Constructor.DeclaringType.FullName == extensionConstructor.DeclaringType.FullName) + { + mayHaveExtensionAttr = true; + break; + } + } + + if (!mayHaveExtensionAttr) + { + continue; + } + + var parameters = method.Parameters; + + if (parameters.Count != 2) + { + continue; + } + + var firstParameterType = parameters[0].ParameterType; + + // ReadValueSafe() and WriteValueSafe() can use both by-ref and non-by-ref type for the first parameter type + var firstParameterElementType = firstParameterType is ByReferenceType byRefType ? byRefType.ElementType : firstParameterType; + + if (firstParameterElementType.FullName != m_FastBufferWriter_TypeRef.FullName && firstParameterElementType.FullName != m_FastBufferReader_TypeRef.FullName) + { + continue; + } + var isExtension = false; foreach (var attr in method.CustomAttributes) { - if (attr.Constructor.Resolve() == extensionConstructor.Resolve()) + if (attr.Constructor.DeclaringType.FullName == extensionConstructor.DeclaringType.FullName && + attr.Constructor.Resolve() == extensionConstructor.Resolve()) { isExtension = true; + break; } } @@ -1005,13 +1047,11 @@ private bool ImportReferences(ModuleDefinition moduleDefinition, string[] assemb continue; } - var parameters = method.Parameters; - - if (parameters.Count == 2 && parameters[0].ParameterType.Resolve() == m_FastBufferWriter_TypeRef.MakeByReferenceType().Resolve()) + if (method.Name == k_WriteValueMethodName && firstParameterType.Resolve() == m_FastBufferWriter_TypeRef.MakeByReferenceType().Resolve()) { m_FastBufferWriter_ExtensionMethodRefs.Add(m_MainModule.ImportReference(method)); } - else if (parameters.Count == 2 && parameters[0].ParameterType.Resolve() == m_FastBufferReader_TypeRef.MakeByReferenceType().Resolve()) + else if (method.Name == k_ReadValueMethodName && firstParameterType.Resolve() == m_FastBufferReader_TypeRef.MakeByReferenceType().Resolve()) { m_FastBufferReader_ExtensionMethodRefs.Add(m_MainModule.ImportReference(method)); } @@ -1861,27 +1901,24 @@ private bool GetWriteMethodForParameter(TypeReference paramType, out MethodRefer { var parameters = method.Resolve().Parameters; - if (method.Name == k_WriteValueMethodName) + if (parameters[1].IsIn) { - if (parameters[1].IsIn) + if (((ByReferenceType)parameters[1].ParameterType).ElementType.FullName == paramType.FullName && + ((ByReferenceType)parameters[1].ParameterType).ElementType.IsArray == paramType.IsArray) { - if (((ByReferenceType)parameters[1].ParameterType).ElementType.FullName == paramType.FullName && - ((ByReferenceType)parameters[1].ParameterType).ElementType.IsArray == paramType.IsArray) - { - methodRef = method; - m_FastBufferWriter_WriteValue_MethodRefs[assemblyQualifiedName] = methodRef; - return true; - } + methodRef = method; + m_FastBufferWriter_WriteValue_MethodRefs[assemblyQualifiedName] = methodRef; + return true; } - else + } + else + { + if (parameters[1].ParameterType.FullName == paramType.FullName && + parameters[1].ParameterType.IsArray == paramType.IsArray) { - if (parameters[1].ParameterType.FullName == paramType.FullName && - parameters[1].ParameterType.IsArray == paramType.IsArray) - { - methodRef = method; - m_FastBufferWriter_WriteValue_MethodRefs[assemblyQualifiedName] = methodRef; - return true; - } + methodRef = method; + m_FastBufferWriter_WriteValue_MethodRefs[assemblyQualifiedName] = methodRef; + return true; } } } @@ -2097,8 +2134,7 @@ private bool GetReadMethodForParameter(TypeReference paramType, out MethodRefere foreach (var method in m_FastBufferReader_ExtensionMethodRefs) { var parameters = method.Resolve().Parameters; - if (method.Name == k_ReadValueMethodName && - parameters[1].IsOut && + if (parameters[1].IsOut && ((ByReferenceType)parameters[1].ParameterType).ElementType.FullName == paramType.FullName && ((ByReferenceType)parameters[1].ParameterType).ElementType.IsArray == paramType.IsArray) { From 456d83cc103f3541e1dac634b1f6292f5ba4b99f Mon Sep 17 00:00:00 2001 From: michal-chrobot Date: Mon, 22 Dec 2025 09:52:46 +0100 Subject: [PATCH 2/2] CHangelog update --- com.unity.netcode.gameobjects/CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/com.unity.netcode.gameobjects/CHANGELOG.md b/com.unity.netcode.gameobjects/CHANGELOG.md index 06109ea401..e550e2f8d7 100644 --- a/com.unity.netcode.gameobjects/CHANGELOG.md +++ b/com.unity.netcode.gameobjects/CHANGELOG.md @@ -13,6 +13,7 @@ Additional documentation and release notes are available at [Multiplayer Documen ### Changed +- Improved performance of NetworkBehaviour ILPostProcessor by omitting unnecessary type and assembly resolutions. ### Deprecated