Fastest Math.Max(value,0)

本文关键字:value Math Max Fastest | 更新日期: 2023-09-27 17:54:49

我有一个算法,它做了很多浮点数/双精度数到0的比较。

像这样:

var sum = 0.0;
for(int i=0;i<data.Length;i++)
  sum += Math.Max(data[i],0);

我想知道是否有更快的方法来做到这一点?在我的微基准测试中,它的执行速度略快(5-7%)。

public static unsafe float FasterCompare(float value)
{
  var val = *(int*) &value;
  return val > 0 ? value : 0;
}

Fastest Math.Max(value,0)

你的当前代码是:

var sum = 0.0;
for(int i=0;i<data.Length;i++)
  sum += Math.Max(data[i],0);

Math.Max是这样做的:

public static double Max(double val1, double val2) 
{
    if (val1 > val2)
        return val1;
    if (Double.IsNaN(val1))
        return val1;
    return val2;
}

如果你知道你不会有NaN,实现你自己的Max没有NaN检查:

public static double Max(double val1, double val2) 
{
    if (val1 > val2)
        return val1;
    return val2;
}

写一个小的基准测试,下面是我的结果:

你的版本:~2.3ms

优化版本:~1.4ms

代码如下:

float[] data = new float[150000];
Random rnd = new Random(12345);
for (int i = 0; i < data.Length; i++)
{
    data[i] = (float)(rnd.NextDouble() * 5000.0 - 2500.0);
}
Stopwatch sw = new Stopwatch();
sw.Start();
var varsum = 0.0;  //varsum is a DOUBLE!!!!
for (int i = 0; i < data.Length; i++)
    varsum += Math.Max(data[i], 0);        //implicit conversions, float->double, int->float
sw.Stop();
Console.WriteLine("Varsum : " + varsum);
Console.WriteLine("Time it took for the original: " + sw.Elapsed.TotalMilliseconds + " ms");
   
float floatsum = 0.0f;
    
sw.Reset();
sw.Start();
floatsum = 0.0f;
for (int i = 0; i < data.Length; i++)
    if (data[i] > 0.0f)
        floatsum += data[i];
sw.Stop();
Console.WriteLine("OptimizedSum: " + floatsum);
Console.WriteLine("Time it took for '"optimized'" version: " + sw.Elapsed.TotalMilliseconds + " ms");
//Equality on floating point numbers doesn't work like this, but...
Console.WriteLine("Are these two equal? " + (floatsum == varsum).ToString());
Console.WriteLine("How close are they? " + Math.Abs(floatsum - varsum).ToString("00.0000000000000000"));
Console.ReadKey(true);

控制台应用程序,32位进程,运行调试模式,编译AnyCPU。第二个版本大约快了60%。这可能是因为不必调用不能内联的方法。

还应该注意的是,这两个方法返回的值不同。这是因为doublefloat没有相同的精度。如果你正在使用浮点数,那就继续使用浮点数。和double一样,不要来回转换。上面的例子给了我超过65的差异!

Fiddle: https://dotnetfiddle.net/S5qmCg(小提琴版本不反映相同的时间值以上,我假设是因为它的资源有限的服务器端,所以尝试在您自己的计算机上)

取决于你的数组有多大(和你的机器)。这在我的机器上要快得多,具体取决于数组的大小:

using System;
using System.Diagnostics;
using System.Linq;
public class Program
{
    public static void Main()
    {
        float[] data = new float[1500000];
        Random rnd = new Random(12345);
        for (int i = 0; i < data.Length; i++)
        {
            data[i] = (float)(rnd.NextDouble() * 5000.0 - 2500.0);
        }
        Stopwatch sw = new Stopwatch();
        sw.Start();
        var varsum = 0.0f;  //varsum is a DOUBLE!!!! change this to 0.0f to make them equal!
        for (int i = 0; i < data.Length; i++)
            varsum += Math.Max(data[i], 0);        //implicit conversions, float->double, int->float
        sw.Stop();
        Console.WriteLine("Varsum : " + varsum);
        Console.WriteLine("Time it took for the original: " + sw.Elapsed.TotalMilliseconds + " ms");
        float floatsum = 0.0f;
        sw.Reset();
        sw.Start();
        floatsum = 0.0f;
        floatsum=data.AsParallel().Where(d=>d>0).Sum();
        sw.Stop();
        Console.WriteLine("OptimizedSum: " + floatsum);
        Console.WriteLine("Time it took for '"optimized'" version: " + sw.Elapsed.TotalMilliseconds + " ms");
        //Equality on floating point numbers doesn't work like this, but...
        Console.WriteLine("Are these two equal? " + (floatsum == varsum).ToString());
        Console.WriteLine("How close are they? " + Math.Abs(floatsum - varsum).ToString("00.0000000000000000"));
    }
}