【问题标题】:Negative to -1, 0 to 0, Positive to 1负到 -1、0 到 0、正到 1
【发布时间】:2011-03-12 08:15:15
【问题描述】:

我有一个枚举MyEnum (Neg -1; None 0; Pos 1)

我使用CompareTo() 的结果来初始化该枚举的对象。

.NET 中最高性能的方法是什么(负值到 -1、0 到 0、正值到 1)?

注意。
CompareTo() 返回一个整数值..

【问题讨论】:

  • @knittl : 你认为 division 是性能最好的吗?
  • @knittl 是否将 .NET 映射到 0/00
  • 如果枚举已经分配了 -1/0/1 值,CompareTo() 将始终返回 -1、0 或 1(从不返回任何其他值)。因此,如果我理解正确,您可以简单地执行以下操作来获取 MyEnum 类型值:MyEnum myval = (MyEnum)x.CompareTo(y);
  • @manixrock:我不确定“总是”。 MSDN讲述:“小于0”:msdn.microsoft.com/en-us/library/…
  • 如果你想要“最高性能”的方法,你需要测试各种选项。看看 System.Diagnostics.Stopwatch 类来衡量每个。

标签: .net


【解决方案1】:

测试结果(双核,x86):

''''''''''''''''''''' DEBUG MODE '''
= 1 =
Division took    00:00:06.2482408 ms
BuiltInSign took 00:00:05.0293383 ms <<<
BitTestSign took 00:00:05.2092181 ms
CustomSign took  00:00:05.2512802 ms

= 2 =
Division took    00:00:06.2477787 ms
BuiltInSign took 00:00:05.0330921 ms <<<
BitTestSign took 00:00:05.2114098 ms
CustomSign took  00:00:05.2556966 ms

= 3 =
Division took    00:00:06.2506690 ms
BuiltInSign took 00:00:05.0388615 ms <<<
BitTestSign took 00:00:05.2306954 ms
CustomSign took  00:00:05.2512391 ms


''''''''''''''''''' RELEASE MODE '''
= 1 =
Division took    00:00:01.0974078 ms
BuiltInSign took 00:00:00.3195232 ms
BitTestSign took 00:00:00.6392142 ms
CustomSign took  00:00:00.3194230 ms <<<

= 2 =
Division took    00:00:01.1007138 ms
BuiltInSign took 00:00:00.3197784 ms <<<
BitTestSign took 00:00:00.6395294 ms
CustomSign took  00:00:00.3202774 ms

= 3 =
Division took    00:00:01.0977087 ms
BuiltInSign took 00:00:00.3194622 ms <<<
BitTestSign took 00:00:00.6394220 ms
CustomSign took  00:00:00.3201607 ms

代码:

public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();
        Stopwatch sw = new Stopwatch();
        MyEnum myEnum = MyEnum.None;

        const int max = 100000000;

        sw.Start();
        for (int i = -max; i < max; i++)
        {
            myEnum = Division(i);
        }
        sw.Stop();
        Console.WriteLine("Division took {0} ms", sw.Elapsed);
        sw.Reset();

        sw.Start();
        for (int i = -max; i < max; i++)
        {
            myEnum = BuiltInSign(i);
        }
        sw.Stop();
        Console.WriteLine("BuiltInSign took {0} ms", sw.Elapsed);
        sw.Reset();

        sw.Start();
        for (int i = -max; i < max; i++)
        {
            myEnum = BitTestSign(i);
        }
        sw.Stop();
        Console.WriteLine("BitTestSign took {0} ms", sw.Elapsed);
        sw.Reset();

        sw.Start();
        for (int i = -max; i < max; i++)
        {
            myEnum = CustomSign(i);
        }
        sw.Stop();
        Console.WriteLine("CustomSign took {0} ms", sw.Elapsed);
    }

    private MyEnum Division(int value)
    {
        return value == 0 ? 0 : (MyEnum)(value / Math.Abs(value));
    }

    private MyEnum BuiltInSign(int value)
    {
        return (MyEnum)Math.Sign(value);
    }

    private MyEnum CustomSign(int value)
    {
        if (value < 0)
            return MyEnum.Neg;

        if (value > 0)
            return MyEnum.Pos;

        return MyEnum.None;
    }

    MyEnum BitTestSign(int value)
    {
        // Shifts the variable over 31 places, 
        // if the MSB is 1, the statement is true
        if ((value >> 31) == 1)
        {
            return MyEnum.Neg;
        }
        else
        {
            if (value == 0)
            {
                return MyEnum.None;
            }
            else
            {
                return MyEnum.Pos;
            }
        }
    }

    private enum MyEnum
    {
        Pos = 1,
        None = 0,
        Neg = -1
    }
}

【讨论】:

  • 在 CustomSign 中,你为什么用return (MyEnum)(-1) 而不是return MyEnum.Pos?为什么MyEnum.Pos 是负数,与您原来的问题相反?
  • 你的测试集不应该也有负数吗?现在,您不要测量负面的性能,它应该是不同的。
  • 在我的电脑上,如果我从 -max 运行到 max,CustomSign 是最快的。
  • CustomSign 在我的机器上也是最快的。对于 -max 到 max,其中 max = 1000000000。Div = 15s,BuiltInSign = 10s,BitTestSign = 10s,CustomSign = 8s。
  • @serhio - 您是否在调试模式而不是发布模式下运行测试?我在调试模式下得到像你一样的结果。您应该尝试在发布模式下运行它们。
【解决方案2】:

.NET 在内部将整数存储为二进制补码。因此,如果您想尝试其他方法,请检查是否设置了最高有效位。这可能更快也可能不会更快,但它应该很容易融入您的测试框架。

伪代码:

if(num == 0)
  return 0;

if(num has msb set)
  return -1;

return 1;

【讨论】:

  • 这个方法不是最优的,看测试结果。
  • 这个方法比我机器上的 Math.Sign 快。
【解决方案3】:

您可以检查变量的最高有效位(在二进制补码中,如果最高有效位为 1,则您有一个负数)。如果您的最高有效位为 0,则检查该值是否为 0。相应地返回一个值。

int Sign(int value){
    if (value >> 31) { //Shifts the variable over 31 places, if the MSB is 1, the statement is true
         return -1;
    } else {
        if (value == 0){
            return 0;
        } else {
            return 1;
        }
    }  }  

(例如编辑)

【讨论】:

  • @GvS:这样想我应该在书里寻找答案,而不是在论坛里问。现在,如果我不知道如何将负数转换为 -1,您认为我能够检查整数中的最高有效位吗? :)
  • @Joren 谢谢,我改了;它现在应该编译为 c++ 代码(这是我的意图,但它看起来像 Java)。
  • 一月,你不需要“else”,因为你确实返回了……现在操作次数后Stevo3000解决方案不是更优化吗?..
  • 没错,'else' 语句是不必要的。至于哪个更优化,我不完全确定——我只是提供一个替代方案。您可以测试哪个更快或允许其他人评论性能差异。我认为这取决于'
  • @Jan:我更多地考虑if (value &gt;&gt; 31 == 1) 以使其成为有效的 C#(一种更可能的 .NET 语言),但可以肯定的是,无论如何。 :)
【解决方案4】:

CompareTo 的结果是负数、零或正数。如果您查看其他答案,Math.Sign 使用 2 个 if 语句返回一个 int。

只需重新编码 Math.Sign 即可返回您的枚举。

(如果这不是家庭作业,我会在那里给出一个代码示例,但你应该从中学习)。

然后,测试一下,看看什么是性能最好的。

【讨论】:

  • 在您等待答案的时间里,您可能已经自己创建了 10 个代码示例,做错了 20 件事,因此学到了 20 个代码示例中没有的东西 ;-)
  • 自定义符号函数比 Math.Sign 更快。
【解决方案5】:

正如@Henrik 和@knittl 所说,您应该使用Math.Sign。但是,如果您对 .Net 框架在幕后所做的事情感兴趣,下面的代码来自 Reflector。

public static int Sign(int value)
{
  if (value < 0)
  {
    return -1;
  }
  if (value > 0)
  {
    return 1;
  }
  return 0;
}

【讨论】:

  • 我对最高效的方法感兴趣。自定义函数的行为是否比内置函数更快?
  • 过早优化是万恶之源
  • serhio,上面的函数内置的。
【解决方案6】:
Math.Sign(value)

【讨论】:

    【解决方案7】:

    看看Math.Sign

    【讨论】:

    • 这个方法效果最好,根据下面的测试结果。
    【解决方案8】:

    .net 框架中有一个名为 Math.Sign 的内置方法

    【讨论】:

      【解决方案9】:

      如果我理解正确,您希望返回 -1 表示负值,0 表示零值,1 表示正值。

      我会选择:

      public static MyEnum GetSign(int value)
      {
          return value == 0 ? 0 : (MyEnum)(value / Math.Abs(value));
      }
      

      还是我做错了什么?

      【讨论】:

      • 首先,CompareTo 返回整数值,其次,我需要最高效的方式。
      • 我们处于 21 世纪。 afaik 整数除法并不比现代 cpus 上的任何其他操作慢。
      • 我猜你会用GetSign(a.compareTo(b));调用这个
      • 有一个输入值会被抛出。
      • @Turing Complete:您如何想象DivisionComparation 在“现代CPU”上以相同的滴答数执行?
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-11-28
      • 2020-08-19
      • 2013-11-05
      • 1970-01-01
      • 1970-01-01
      • 2019-10-03
      相关资源
      最近更新 更多