使用C#类型表示度量单位

时间:2010-11-10 14:02:15

标签: c# performance struct operator-keyword

我试图通过将double包装到struct中来获得我称之为测量单位系统的东西。我有C#结构,如Meter,Second,Degree等。我最初的想法是,在编译器内联所有内容后,我的性能与使用double时相同。

我的显式和隐式运算符简单明了,编译器确实内联它们,但是Meter和Second的代码比使用double的相同代码慢10倍。

我的问题是:为什么C#编译器不能使用Second作为使用double的代码的最佳代码,如果它无论如何都要内联?

第二个定义如下:

struct Second
{
    double _value; // no more fields.

    public static Second operator + (Second left, Second right) 
    { 
        return left._value + right._value; 
    }
    public static implicit Second operator (double value) 
    { 
        // This seems to be faster than having constructor :)
        return new Second { _value = value };
    }

    // plenty of similar operators
}

更新

我没有问过结构是否适合这里。确实如此。

我没有询问代码是否会被内联。 JIT会内联它。

我检查了运行时发出的汇编操作。对于像这样的代码,它们是不同的:

var x = new double();
for (var i = 0; i < 1000000; i++)
{ 
    x = x + 2;
    // Many other simple operator calls here
}

并且像这样:

var x = new Second();
for (var i = 0; i < 1000000; i++)
{ 
    x = x + 2;
    // Many other simple operator calls here
}

反汇编中没有调用指令,因此操作实际上是内联的。然而,差异很大。性能测试表明,使用Second比使用double要慢10倍。

所以我的问题是(注意!):为什么JIT生成的IA64代码对于上述情况有所不同?如何使struct运行速度与double一样快?似乎双重和第二之间没有理论上的区别,我看到差异的深层原因是什么?

2 个答案:

答案 0 :(得分:4)

这是我的意见,如果你不同意,请写下评论,而不是沉默的downvoting。

C#编译器没有内联它。 JIT编译器可能,但这对我们来说是不确定的,因为JITer的行为并不简单。

如果double没有实际调用任何运算符。使用操作码add将操作数添加到堆栈中。在您的情况下,调用方法op_Add加上三个struct复制到堆栈。

要优化它,请先将struct替换为class。它至少可以减少副本量。

答案 1 :(得分:1)

C#编译器没有内联任何 - JIT 可能这样做,但不必。它应该仍然快速充足。我可能会删除+中的隐式转换(请参阅下面的构造函数用法) - 另外一个要查看的运算符:

private readonly double _value;
public double Value { get { return _value; } }
public Second(double value) { this._value = value; }
public static Second operator +(Second left, Second right) {
    return new Second(left._value + right._value);
}
public static implicit operator Second(double value)  {
    return new Second(value);
}

JIT内联仅限于特定场景。这段代码会满足他们吗?很难说 - 但是对于大多数情况,它应该工作并且工作得足够快+的问题是有一个用于添加双精度的IL操作码;它几乎没有工作 - 你的代码在哪里调用一些静态方法和一个构造函数;即使在内联时,总会有一些开销。