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;
}
你的当前代码是:
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%。这可能是因为不必调用不能内联的方法。
还应该注意的是,这两个方法返回的值不同。这是因为double
和float
没有相同的精度。如果你正在使用浮点数,那就继续使用浮点数。和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"));
}
}