Меньше, чем оператор против Math.Abs() для проверки отрицательного значения

#c#

#c#

Вопрос:

В приведенном ниже примере есть ли какое-либо преимущество (с точки зрения производительности) в использовании оператора меньше, чем ( < ), чтобы проверить, является ли значение отрицательным по сравнению с использованием Math.Abs?

 int value = 0;

if(value < 0){
    // Value is negative
}

if(Math.Abs(value) != value){
    // Value is negative
}
  

Комментарии:

1. Я сомневаюсь, что разница будет заметна, но оператор C # < быстрее, чем метод .NET Framework Math.Abs . Почему бы вам не проверить это самостоятельно?

2. Использование `< ` vs ` != ` не имеет никакого значения для процессора. Поэтому выполнение чего-то дополнительного, а затем сравнение всегда будет медленнее.

3. @JohnHarper ты шутишь, или? Что лучше выражает «отрицательный»: «меньше 0» или «не равно его абсолютному значению»?

Ответ №1:

глядя на исходный код Abs :

   public static int Abs(int value) {
      if (value >= 0)
          return value;
      else
          return AbsHelper(value);        
  }
    
  private static int AbsHelper(int value) {
      Contract.Requires(value < 0, "AbsHelper should only be called for negative values! (hack for JIT inlining)");
      if (value == Int32.MinValue)
          throw new OverflowException(Environment.GetResourceString("Overflow_NegateTwosCompNum"));
      Contract.EndContractBlock();
      return -value;
  }
  

Он не только будет медленнее, но и будет иметь другое поведение для вызова Math.Abs вместо < и выдаст исключение для значения Int32.MinValue (-2147483648)

Ответ №2:

Оператор меньше, чем быстрее:

 using System;
using System.Diagnostics;

namespace so64610522
{
    class Program
    {
        static bool IsNeg1(int a)
        {
            return a < 0;
        }

        static bool IsNeg2(int a)
        {
            return (Math.Abs(a) != a);
        }

        static void Main(string[] args)
        {
            var min = -300000000;
            var max =  300000000;
            var sw = new Stopwatch();
            sw.Start();
            for (int i = min; i < max; i  )
            {
                IsNeg1(i);
            }
            var t1 = sw.ElapsedMilliseconds;
            sw.Restart();
            for (int i = min; i < max; i  )
            {
                IsNeg2(i);
            }
            var t2 = sw.ElapsedMilliseconds;
            Console.WriteLine("{0} vs {1}", t1, t2);
        }
    }
}
  

На моей машине это выводит

 1761 vs 2608
  

таким образом, оператор less-than на ~ 50% быстрее, чем метод abs.

Ответ №3:

Распространенный способ сравнения производительности — посмотреть, сколько операций может быть выполнено за промежуток времени. Вы могли бы легко написать что-нибудь для сравнения этих двух методов (несмотря на то, что они дают разные результаты!)

 public static async Task Main()
{
    var value = 100;
    var result1 = await Perf(() => {var x = value < 0;});
    Console.WriteLine(result1); 
    
    var result2 = await Perf(() => {var x = Math.Abs(value) != value;});
        
    Console.WriteLine(result2);
}

static async Task<int> Perf(Action act)
{
    int numOps = 0;
    
    var cts = new CancellationTokenSource();
    
    await Task.WhenAny(Task.Delay(2000),Task.Run(() => {
        while(true)
        {
            act();
            numOps  ;
        }
    },cts.Token));
    
    cts.Cancel();
    
    return numOps;
}
  

Живой пример: https://dotnetfiddle.net/tgascX

Результаты, которые я получил за 2 секунды, были:

  • < = 501726838
  • Math.Abs = 339073849

Ускорение использования < примерно на 48%