将数量封装在类型安全的结构中是否会影响性能?

时间:2014-09-09 18:12:28

标签: c# performance struct units-of-measurement

我使用这个约定(灵感来自F#的单位)来捕捉某些类别的编程错误:

public struct Inch : IComparable<Inch>
{
    public readonly float Value;
    public Inch(int value) : this() { Value = value; }
    public static implicit operator Inch(float value) { return new Inch(value); }
    public int CompareTo(Inch other) { return Value.CompareTo(other.Value); }
    public override string ToString() { return Value.ToString(); }
}

这样进行添加等操作:

Inch a, b;
Inch result = a.Value + b.Value;

这允许以值类型的低开销传递Inch,其优点是不会意外地将其分配给普通float。 (我发现允许在相反方向进行隐式转换,即浮点数到Inch es,通常不会导致错误。)

问题:显示的添加示例是否存在已知的性能问题具体。同样,这个问题只是关于性能,我对语义没有任何疑问。

2 个答案:

答案 0 :(得分:1)

这里最大的语义问题是使用implicit运算符,它允许将任何float值视为Inch单位。您应该删除该运算符,或将其更改为explicit

最大的性能问题是缺少被覆盖的EqualsGetHashCode方法。您应该在default implementation provided by ValueType which may be slower slower上覆盖这两种方法。某些运行时环境可能会检测到该类没有直接或间接包含任何带引用类型的字段,并提供了这些方法的有效实现,但不保证这一点。

要获得额外的可用性,您可以定义执行添加的operator +,以便您可以拥有以下内容:

Inch a, b;
Inch result = a + b;

当然,这同样适用于operator -

理论上,结构可能会使性能开销接近零,但它将依赖于运行时环境的许多方面,尤其是内联方法的能力。关于开销是否可观察和/或可接受的最终答案只能通过在预期环境中使用预期输入来分析类型的使用来确定。

答案 1 :(得分:1)

我在LINQPad 4中使用完全优化测试了以下代码:

    struct IntWrapper
    {
        readonly int x;

        public IntWrapper(int x)
        {
            this.x = x;
        }

        //[MethodImpl(MethodImplOptions.AggressiveInlining)] doesn't seem to matter
        public static implicit operator IntWrapper(int x)
        {
            return new IntWrapper(x);
        }

        //[MethodImpl(MethodImplOptions.AggressiveInlining)] doesn't seem to matter
        public static IntWrapper operator +(IntWrapper x, IntWrapper y)
        {
            return new IntWrapper(x.x + y.x);
        }
    }

    void Main()
    {
        var random = new Random();
        var sw = new Stopwatch();
        var xs = new List<int>(500);
        var mode = new Dictionary<int, int>();

        for (var j = 0; j < 500; j++)
        {
            sw.Start();

            for (var i = 0; i < 1000000; i++)
            {
                var x = random.Next();
                var y = random.Next();

                var z = x + y;
            }

            sw.Stop();

            var elasped = (int)sw.ElapsedMilliseconds;

            xs.Add(elasped);

            int count;

            if(!mode.TryGetValue(elasped, out count))
                mode.Add(elasped, 1);
            else
                mode[elasped] = count + 1;

            Console.WriteLine(elasped);

            sw.Reset();
        }

        Console.WriteLine(xs.Average());
        Console.WriteLine(Math.Sqrt(xs.Select(x => Math.Pow(x, 2)).Sum() / (xs.Count())));

        var max = 0;
        var key = 0;

        foreach (var memo in mode)
            if(memo.Value > max)
            {
                max = memo.Value;
                key = memo.Key;
            }

        Console.WriteLine(key);

    }

System.Int32IntWrapper的情况下,我观​​察到500次试验的平均值(平均值)约为30ms,模式为25ms

有趣的是,我记得曾在某处读到ValueType类的readonly字段上的ValueType修饰符会导致性能下降,但我在此实验中没有发现这样的惩罚。