// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
using System.Numerics;
using BenchmarkDotNet.Attributes;
using SixLabors.ImageSharp.Common.Helpers;
namespace SixLabors.ImageSharp.Benchmarks.General.BasicMath;
///
/// Exposes every floating-point tensor compatibility operation for assembly inspection.
///
[Config(typeof(Config.Analysis))]
public class TensorPrimitivesAssembly
{
private const int Count = 2048;
private readonly float[] x = new float[Count];
private readonly float[] y = new float[Count];
private readonly float[] destination = new float[Count];
///
/// Populates the input spans with deterministic non-uniform values.
///
[GlobalSetup]
public void Setup()
{
for (int i = 0; i < Count; i++)
{
this.x[i] = ((i * 17) % 251) + 1;
this.y[i] = ((i * 29) % 251) + 1;
}
}
///
/// Adds two floating-point spans.
///
/// The first result, which keeps the destination observable.
[Benchmark]
public float Add()
{
TensorPrimitives_.Add(this.x, this.y, this.destination);
return this.destination[0];
}
///
/// Clamps a floating-point span between scalar bounds.
///
/// The first result, which keeps the destination observable.
[Benchmark]
public float Clamp()
{
TensorPrimitives_.Clamp(this.x, 64F, 128F, this.destination);
return this.destination[0];
}
///
/// Divides a floating-point span by a scalar.
///
/// The first result, which keeps the destination observable.
[Benchmark]
public float Divide()
{
TensorPrimitives_.Divide(this.x, 4096F, this.destination);
return this.destination[0];
}
///
/// Computes the element-wise maximum of a floating-point span and a scalar.
///
/// The first result, which keeps the destination observable.
[Benchmark]
public float Max()
{
TensorPrimitives_.Max(this.x, 64F, this.destination);
return this.destination[0];
}
///
/// Multiplies a floating-point span by a scalar.
///
/// The first result, which keeps the destination observable.
[Benchmark]
public float Multiply()
{
TensorPrimitives_.Multiply(this.x, 0.5F, this.destination);
return this.destination[0];
}
}
///
/// Exposes integral addition specializations for assembly inspection.
///
/// The integral element type.
[Config(typeof(Config.Analysis))]
[GenericTypeArguments(typeof(byte))]
[GenericTypeArguments(typeof(uint))]
public class TensorPrimitivesIntegralAddAssembly
where T : unmanaged, INumber
{
private const int Count = 2048;
private readonly T[] x = new T[Count];
private readonly T[] y = new T[Count];
private readonly T[] destination = new T[Count];
///
/// Populates the input spans with deterministic non-uniform values.
///
[GlobalSetup]
public void Setup()
{
for (int i = 0; i < Count; i++)
{
this.x[i] = T.CreateTruncating((i * 17) + 31);
this.y[i] = T.CreateTruncating((i * 29) + 7);
}
}
///
/// Adds two integral spans.
///
/// The first result, which keeps the destination observable.
[Benchmark]
public T Add()
{
TensorPrimitives_.Add(this.x, this.y, this.destination);
return this.destination[0];
}
}
///
/// Exposes integral clamp specializations for assembly inspection.
///
/// The integral element type.
[Config(typeof(Config.Analysis))]
[GenericTypeArguments(typeof(byte))]
[GenericTypeArguments(typeof(uint))]
[GenericTypeArguments(typeof(int))]
public class TensorPrimitivesIntegralClampAssembly
where T : unmanaged, INumber
{
private const int Count = 2048;
private readonly T[] source = new T[Count];
private readonly T[] destination = new T[Count];
private T min;
private T max;
///
/// Populates the input span and scalar bounds with deterministic values.
///
[GlobalSetup]
public void Setup()
{
this.min = T.CreateTruncating(64);
this.max = T.CreateTruncating(128);
for (int i = 0; i < Count; i++)
{
this.source[i] = T.CreateTruncating((i * 31) % 257);
}
}
///
/// Clamps an integral span between scalar bounds.
///
/// The first result, which keeps the destination observable.
[Benchmark]
public T Clamp()
{
TensorPrimitives_.Clamp(this.source, this.min, this.max, this.destination);
return this.destination[0];
}
}