【问题标题】:Fastest way to check the numerical value of a boxed primitive integral type in C# [closed]在 C# 中检查盒装原始整数类型的数值的最快方法 [关闭]
【发布时间】:2014-08-18 22:06:41
【问题描述】:

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

/// <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;
}

您能提出更好的方法吗?

【问题讨论】:

  • 您的代码目前似乎可以运行,并且您正在寻求改进它。一般来说,这些问题对于本网站来说过于固执己见,但您可能会在CodeReview.SE 找到更好的运气。记得阅读their requirements,因为他们比这个网站更严格。
  • 这个问题似乎是题外话,因为它已经包含一个有效的解决方案。要请求对有效解决方案的批评,请考虑在 Code Review 上提问。
  • AFAIK 使这更快的唯一方法是消除装箱和拆箱。
  • @Serve 要求是生成最快的代码来解决问题。我的解决方案是否满足此要求并不是很明显,因此它可能无法“正常工作”。
  • @JeroenVannevel:在传递对象之前,您仍然需要一个 case 语句来将对象转换为正确的类型,因此您只是将复杂性推到了其他地方。

标签: c# .net performance equality primitive


【解决方案1】:

我没有测试过性能,但它更短:

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&lt;T&gt; ab使用,所以它可能比你的解决方案最慢。

[编辑]

我忘了还有一个好处:没有is 测试。你最坏的情况是 1 is 和 2 演员。所以也许表演可以足够好。

[编辑 2]

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

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

    ...
}

【讨论】:

    【解决方案2】:

    由于每个整数原始类型都是密封的,您的参考实现执行的 is 操作由 JIT 编译,以简单地在对象 x 的类型的 RuntimeTypeHandle 之间进行相等比较(可能获得通过一个或两个指针取消引用)和特定整数类型的句柄(可能实现为内联文字或引用固定内存位置的单个 mov 指令)。如果不利用有关方法输入分布的信息,即使不是不可能,也很难改进初始实现。

    【讨论】:

    • 你到底为什么要对同一个问题发布多个答案?
    【解决方案3】:

    在原始问题的限制下,我肯定会测试的当前实现的替代方案如下。根据代表原始类型的 .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;
      }
    }
    

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

    【讨论】:

    • 这与 OP 的代码没有什么不同。
    • 这不适用于枚举,并且比我最初的解决方案慢。
    • @Servy 如果输入是ushort,它会避免5次类型检查,有利于2次虚拟调用。
    • @280Z28 仅此而已,它不会避免这 5 种类型检查。他们仍然会在那里。
    • @Servy 我更新了我的答案。运行时可以(但不是必须)以不会导致这些类型检查的方式实现Type.GetTypeCodeImpl。根据 Vladimir 提供的信息,当前的实现没有针对这种情况进行优化,并且不能证明比原始代码更快。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-09-22
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多