为了对现有软件框架进行优化准备,我进行了独立的性能测试,以便在花费大量时间之前评估潜在收益。
情况如下: 共有N种不同类型的组件,其中一些实现了IUpdatable接口,这些是有趣的组件。它们分组在M个对象中,每个对象维护一个组件列表。更新它们的工作方式如下:
## 问题
情况如下: 共有N种不同类型的组件,其中一些实现了IUpdatable接口,这些是有趣的组件。它们分组在M个对象中,每个对象维护一个组件列表。更新它们的工作方式如下:
foreach (GroupObject obj in objects)
{
foreach (Component comp in obj.Components)
{
IUpdatable updatable = comp as IUpdatable;
if (updatable != null)
updatable.Update();
}
}
优化
我的目标是针对大量分组对象和组件进行优化更新。首先,确保按照种类将所有组件逐一更新,通过将它们缓存在每种组件的一个数组中。基本上,就是这样:
foreach (IUpdatable[] compOfType in typeSortedComponents)
{
foreach (IUpdatable updatable in compOfType)
{
updatable.Update();
}
}
这个想法的背后是,JIT或CPU在重复操作同一对象类型时,与在随机版本中进行操作相比,可能更容易操作。
在下一步中,我想进一步改善情况,确保一个组件类型的所有数据都能够在内存中对齐 - 通过将它存储在一个结构体数组中,类似于这样:
foreach (ComponentDataStruct[] compDataOfType in typeSortedComponentData)
{
for (int i = 0; i < compDataOfType.Length; i++)
{
compDataOfType[i].Update();
}
}
## 问题
在我的独立性能测试中,这些更改都没有带来显著的性能提升。我不确定为什么。没有显著的性能提升意味着,在10000个组件,每个批次运行100个更新周期时,所有主要测试需要大约85毫秒+/- 2毫秒。
(唯一的区别是引入了as
强制转换和if
检查,但这并不是我要测试的内容。)
- All tests were performed in Release mode, without attached debugger.
External disturbances were reduced by using this code:
currentProc.ProcessorAffinity = new IntPtr(2); currentProc.PriorityClass = ProcessPriorityClass.High; currentThread.Priority = ThreadPriority.Highest;
Each test actually did some primitive math work, so it's not just measuring empty method calls which could potentially be optimized away.
- Garbage Collection was performed explicitly before each test, to rule out that interference as well.
- The full source code (VS Solution, Build & Run) is available here
考虑到内存对齐和更新模式的重复,我本来期望会有显著的变化。因此,我的核心问题是:为什么我无法测量到显著的改进?我是否忽视了一些重要的事情?在测试中是否遗漏了某些内容?