今天在尝试优化时遇到了一件比较奇怪的事情。我使用 Monogame 框架并想即时创建纹理。我在 Visual Studio Community 2022 中使用 .NET 6。
所以这是执行此操作的直接代码,工作正常:
public static Texture2D CreateTexture(GraphicsDevice graphicsDevice, int width, int height)
{
Texture2D tex = new(graphicsDevice, width, height, true, SurfaceFormat.Color);
Color color = new((byte)255, (byte)255, (byte)255, (byte)255);
int length = width * height;
Color[] colorArray = new Color[length];
for (int i = 0; i < length; i++)
{
color.A = (byte)randomizer.Next(0, 256);
colorArray[i] = color;
}
tex.SetData<Color>(colorArray);
return tex;
}
由于我多次调用这个方法,通常是相同的宽度和高度,我想我可以做一些内存优化,最后得到类似的代码:
static int texW = 1;
static int texH = 1;
static Color[] colorArray = new Color[1];
public static Texture2D CreateTexture2(GraphicsDevice graphicsDevice, int width, int height)
{
Texture2D tex = new(graphicsDevice, width, height, true, SurfaceFormat.Color);
int length = width * height;
if (texW != width || texH != height)
{
texW = width;
texH = height;
colorArray = new Color[length];
}
for (int i = 0; i < length; i++)
{
colorArray[i].A = (byte)randomizer.Next(0, 256);
colorArray[i].R = 255;
colorArray[i].G = 255;
colorArray[i].B = 255;
}
tex.SetData<Color>(colorArray);
return tex;
}
如您所见,我尽量避免在每次调用函数时都分配 colorArray。由于数组是 Color-struct-objects 的列表,我假设我应该用 new 替换每个结构对象中的 R、G、B 和 A 值,而不是替换整个结构。
为了测试它,我尝试在单独的调试会话中每秒调用这两个函数数千次,每次大约三十秒。
我希望我的改变能改善各种与记忆相关的事情,但没有!相反,我的第二个例子真的让垃圾收集器感到不安,它开始频繁地进行 GC,视觉工作室的“诊断工具”显示分配的对象是原来的两倍,而且帧率似乎也下降了。
有人知道这里发生了什么吗?我的第二个代码示例分配了 colorArray one 时间,正如我用相同的宽度和高度调用它数千次时所预期的那样。此外,为字节类型的 R、G、B 和 A 成员赋值不应导致任何内存分配。
正如杰里米在评论中指出的那样,罪魁祸首是 Texture2D。在我的测试代码中绘制纹理后,我没有在纹理上调用 Dispose()。不好意思,怪我看不懂文档
现在,当我调用 Dispose() 时,代码按预期工作。但是我对这两段代码之间的微小差异感到有点惊讶;为什么第一段代码不会比它更让 GC 不安?在我的简单测试中,我看不出真正的区别。