在C#中检查盒装基元积分类型数值的最快方法

时间:2014-06-27 17:45:17

标签: c# .net performance equality primitive

我需要编写一个带有以下语义的方法:

/// <summary>
/// Checks if <paramref name="x"/> is a boxed instance of a primitive integral type
/// whose numerical value equals to <paramref name="y"/>.
/// </summary>
/// <param name="x">An object reference. Can be <c>null</c>.</param>
/// <param name="y">A numerical value of type <see cref="ulong"/> to compare with.</param>
/// <returns>
/// <c>true</c> if <paramref name="x"/> refers to a boxed instance of type 
/// <see cref="sbyte"/>, <see cref="short"/>, <see cref="int"/>, <see cref="long"/>, 
/// <see cref="byte"/>, <see cref="ushort"/>, <see cref="uint"/>, or <see cref="ulong"/>, 
/// whose numerical value equals to the numerical value of <paramref name="y"/>; otherwise, <c>false</c>.
/// </returns>
/// <remarks>
/// <para>
/// This method checks only for numeric equality, even if its arguments are of different runtime types
/// (e.g. <c>2L</c> is considered to be equal to <c>2UL</c>).
/// </para>
/// <para>
/// This method returns <c>false</c> if <paramref name="x"/> is <c>null</c>
/// or refers to an instance of a reference type or a boxed instance of a value type except
/// the primitive integral types listed above (e.g. it returns <c>false</c> if <paramref name="x"/>
/// refers to a boxed instance of an <c>enum</c> type, <see cref="bool"/>, <see cref="char"/>, <see cref="IntPtr"/>,
/// <see cref="UIntPtr"/>, <see cref="float"/>, <see cref="double"/>, <see cref="decimal"/>, or <see cref="BigInteger"/>).
/// </para>
/// <para>
/// This method should not throw any exceptions, or cause any observable side-effects
/// (e.g. invoke a method that could modify the state of an object referenced by <paramref name="x"/>). 
/// </para>
/// </remarks>
[Pure]
public static bool NumericalEquals(object x, ulong y)

实现应该尽可能快(假设输入数据没有预期的偏向某些类型或参数x的值),并且不应该使用unsafe代码或P / Invoke 。当然,在最快的实现中,我更喜欢最简单和最短的实现。

我的解决方案如下:

public static bool NumericalEquals(object x, ulong y)
{
    if (x is sbyte)
    {
        sbyte z = (sbyte)x;
        return z >= 0 && y == (ulong)z;
    }

    if (x is short)
    {
        short z = (short)x;
        return z >= 0 && y == (ulong)z;
    }

    if (x is int)
    {
        int z = (int)x;
        return z >= 0 && y == (ulong)z;
    }

    if (x is long)
    {
        long z = (long)x;
        return z >= 0 && y == (ulong)z;
    }

    if (x is byte)
    {
        return y == (byte)x;
    }

    if (x is ushort)
    {
        return y == (ushort)x;
    }

    if (x is uint)
    {
        return y == (uint)x;
    }

    if (x is ulong)
    {
        return y == (ulong)x;
    }

    return false;
}

你能建议一个更好的方法吗?

3 个答案:

答案 0 :(得分:2)

由于每个整数基元类型都是密封的,因此引用实现执行的is操作由JIT编译,只是对象类型的RuntimeTypeHandle之间的相等比较{{ 1}}(可能通过一个或两个指针解引用获得)和特定整数类型的句柄(可能实现为内联文字或引用固定内存位置的单个x指令)。如果不是不可能的话,在不利用有关方法输入分布的信息的情况下,改进初始实施将是困难的。

答案 1 :(得分:0)

我没有测试过表演但是时间较短:

public static bool NumericalEquals(object x, ulong y)
{
    var unsigned = (x as byte?) ?? (x as ushort?) ?? (x as uint?) ?? (x as ulong?);
    if (unsigned.HasValue)
    {
        return (unsigned.Value == y);
    }

    var signed = (x as sbyte?) ?? (x as short?) ?? (x as int?) ?? (x as long?);
    return (signed.HasValue) && (signed.Value >= 0) && ((ulong) signed.Value == y);
}

在最糟糕的情况下,会有3个强制转换(例如objectsbyte?long?然后是值ulong),我不会谈论Nullable<T> ab 使用,所以它可能比你的解决方案最慢。

<强> [编辑]

我忘记了奖励:没有is测试。最糟糕的情况是1 is和2投。所以表演也许还不错。

[编辑2]

您也可以考虑快速消除测试以提高平均成本:

public static bool NumericalEquals(object x, ulong y)
{
    if (x.GetHashCode() != y.GetHashCode()) return false;

    ...
}

答案 2 :(得分:-2)

在原始问题的限制下,我肯定会测试您当前实现的替代方案如下。根据表示基本类型的.NET Framework类所使用的Type.GetTypeCodeImpl的实现,通过在2个虚拟调用的结果上用switch替换最多5个类型检查,可以更快地证明此操作(加一个处理枚举)。它使用TypeCode枚举包含每个基本整数类型的成员的事实。

public static bool NumericalEquals(object x, ulong y)
{
  if (x == null)
    return false;

  Type type = x.GetType();
  if (type.IsEnum)
    return false;

  switch (Type.GetTypeCode(type))
  {
  case TypeCode.Byte:
    return (byte)x == y;

  ...other cases here

  default:
    return false;
  }
}

您可以通过将块包装在IsEnum / try中来处理只有在提供盒装枚举作为输入时才会抛出的异常,从而避免对catch的调用。这可能会提高预期案例的性能,只需花费较少的false盒装枚举结果。