.NET IL 字节码优化工具

7
我正在尝试编写优化的C# .NET和Mono代码,用于游戏开发。(是的,我有使用C#而不是C++的充分理由。)
我注意到C#似乎无法正确地优化其运算符。与使用Vector4数学手动内联代码相比,运算符的速度慢了一倍。
以下是我在.NET 4.5中进行的一些简单基准测试,重复9999999次:
// Test 1
for (uint i = 0; i != 9999999; ++i)// 230 MS
{
  vector += vector2;
  vector2 -= vector;
}

// Test 2
for (uint i = 0; i != 9999999; ++i)// 185 MS
{
  vector = vector.Add(ref vector2);
  vector2 = vector2.Sub(ref vector);
}

// Test 3
for (uint i = 0; i != 9999999; ++i)// 116 MS
{
  vector.X += vector2.X;
  vector.Y += vector2.Y;
  vector.Z += vector2.Z;
  vector.W += vector2.W;
  vector2.X -= vector1.X;
  vector2.Y -= vector1.Y;
  vector2.Z -= vector1.Z;
  vector2.W -= vector1.W;
}

// EDIT Test 1 SOLVED ----------------------------------
// When the Operators are created like so, they actually perform the BEST!
// Sry MS for complaining :(...  Although SIMD support would be nice :)
struct Vector4
{
    public static Vector4 operator +(Vector4 p1, Vector4 p2)
{
    p1.X += p2.X;
    p1.Y += p2.Y;
    p1.Z += p2.Z;
    p1.W += p2.W;
    return p1;
}

public static Vector4 operator -(Vector4 p1, Vector4 p2)
{
    p1.X -= p2.X;
    p1.Y -= p2.Y;
    p1.Z -= p2.Z;
    p1.W -= p2.W;
    return p1;
}
}

for (uint i = 0; i != 9999999; ++i)// 75 MS
{
  vector += vector2;
  vector2 -= vector;
}

我想知道是否有任何.NET IL优化工具?我已经搜索了,但还没有找到合适的。或者更清晰地说,有没有办法优化我的C#代码或IL代码以提高性能。
我真的希望看到运算符至少在185ms内执行。这是很有意义的。
这里是我用于测试的应用程序链接: 下载

你正在调试器下运行吗? - Reed Copsey
@ReedCopsey 我是通过VS-2012编译应用程序,然后通过资源管理器执行控制台应用程序。我正在使用Stopwatch来测量时间,甚至将“TimeBeginPeriod(1)”设置为ONE以确保Stopwatch非常准确。 - zezba9000
4
我把你在微软方面的评论删除了,因为它不属实,并且我对此过敏。请在Stack Overflow上保持中立。 - usr
5
Test1和Test2正在创建额外的对象。我认为这就是它们变慢的原因,而不是因为IL未进行优化... - Paolo Tedesco
2
顺便提一下,在 .net 4.5 中,您可以使用 MethodImplOptions.AggressiveInlining 来鼓励内联。 - CodesInChaos
显示剩余14条评论
2个回答

3
你自己的回答和评论都暗示了为什么调用.Add比使用加法运算符更快。
"+"的语义是操作数不变。做1 + 2,你不会期望1之后的值变成3吧?因此,为了遵循最少惊奇原则,各种实现中的加法运算符都遵循这个语义。
这也意味着vector4的加法运算符需要创建一个新的Vector4对象。这个新对象的内存可能已经分配(例如栈),但这并没有什么帮助,因为当它被分配给任何返回类型时,我们仍然必须复制该值。
Add实例方法的语义与加法运算符不同。它改变一个实例,因此不必创建新对象。
你在发布的答案中关于加法运算符的语义等价于add。

为什么创建一个新的 Vector4 会耗费时间?它是一种值类型。而且你为什么要谈论 V-table 查找呢? - CodesInChaos
@CodesInChaos 因为内存分配不是免费的。 - Rune FS
当你执行 new SomeValueType 时,其实并没有真正的内存分配。在机器代码层面上,OP 的 += 代码与基于 new Vector3(...) 的代码产生不同的结果也没有真正的理由。 - CodesInChaos
@RuneFS 我理解你所说的情况,这也是为什么 IL 优化器可以帮助清理各个地方的小问题的原因。 - zezba9000
@CodeInChaos,您如何将堆栈中的结果副本消除掉?(或另一个堆栈帧)我无法看出在不改变程序语义的情况下如何从机器代码中消除它。 - Rune FS

2
我发现这个解决方案可以解决我的问题...虽然我仍然希望能获得关于.NET IL优化器的任何信息。此外,了解为什么使用运算符比手动倾斜代码更快可能会很有趣?
要在.NET上实现最佳的Vector4操作性能,请按照以下步骤进行:
public static Vector4 operator +(Vector4 p1, Vector4 p2)
{
    p1.X += p2.X;
    p1.Y += p2.Y;
    p1.Z += p2.Z;
    p1.W += p2.W;
    return p1;
}

public static Vector4 operator -(Vector4 p1, Vector4 p2)
{
    p1.X -= p2.X;
    p1.Y -= p2.Y;
    p1.Z -= p2.Z;
    p1.W -= p2.W;
    return p1;
}

不要这样做:

public static Vector4 operator +(Vector4 p1, Vector4 p2)
{
    return new Vector4(p1.X+p2.X, p1.Y+p2.Y, p1.Z+p2.Z, p1.W+p2.W);
}
//ect...

为什么代码这么丑陋?一个简单的 return new Vector4(p1.X+p2.X,...) 可能同样快,但更加优美。你不需要真正创建值类型的实例。 - CodesInChaos
1
@CodesInChaos,这就是我做的事情,速度非常慢。230毫秒对比75毫秒。你会选择哪一个? - zezba9000
1
奇怪。优化器似乎表现得很愚蠢。没有理由这样会更快。我需要调查一下。 - CodesInChaos
@CodeInChaos,所以值类型不需要分配内存?这让我很惊讶。那它们存储在哪里呢? - Rune FS
1
@RuneFS 值类型的局部变量会大多数情况下存储在堆栈(或某些情况下是寄存器)中。 - svick
1
@svick 这真的不能改善情况,不是吗? 首先,我们将该值推送到堆栈上,然后将其复制到分配给返回值的任何内容中。 - Rune FS

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接