在结构中封装一个量以确保类型安全是否会影响性能



我使用这个约定(受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。(我发现,允许在相反的方向上进行隐式转换,即将浮点转换为Inches,通常不会导致错误。)

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

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

最大的性能问题是缺少重写的EqualsGetHashCode方法。您应该在ValueType提供的默认实现上覆盖这两种方法,后者可能更慢。一些运行时环境可能会检测到类不直接或间接包含任何具有引用类型的字段,并提供这些方法的有效实现,但对此没有任何保证。

为了获得额外的可用性,您可以定义一个执行添加的operator +,因此您可以拥有以下内容:

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

当然,operator -也是如此。

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

我在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`类的ValueType字段上的readonly修饰符会导致性能下降,但我在这个实验中没有观察到这样的惩罚。

最新更新