// 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]; } }