// Copyright (c) Six Labors. // Licensed under the Six Labors Split License. using BenchmarkDotNet.Attributes; using BenchmarkDotNet.Columns; using BenchmarkDotNet.Configs; using SixLabors.ImageSharp.Formats.Heif.Av1.Transform; using SixLabors.ImageSharp.Formats.Heif.Av1.Transform.Forward; using SixLabors.ImageSharp.Formats.Heif.Av1.Transform.Inverse; namespace SixLabors.ImageSharp.Benchmarks.Codecs.Heif; /// /// Compares scalar, SIMD, and runtime-dispatched AV1 forward and inverse transform blocks. /// [MemoryDiagnoser(displayGenColumns: false)] [GroupBenchmarksBy(BenchmarkLogicalGroupRule.ByCategory)] [CategoriesColumn] public class Av1TransformBenchmarks { private readonly short[] spatial = new short[32 * 32]; private readonly int[] coefficients = new int[32 * 32]; private readonly byte[] prediction = new byte[32 * 32]; private readonly byte[] reconstruction = new byte[32 * 32]; private readonly int[] workspace = new int[Av1TransformWorkspace.MaximumLength]; /// /// Initializes deterministic residual, coefficient, and prediction buffers outside the measured operations. /// [GlobalSetup] public void Setup() { for (int index = 0; index < this.spatial.Length; index++) { this.spatial[index] = (short)(((index * 73) % 1023) - 511); this.coefficients[index] = ((index * 37) % 129) - 64; this.prediction[index] = (byte)(64 + ((index * 29) % 128)); } } /// /// Measures the scalar eight-by-eight forward DCT traversal. /// /// The last coefficient written by the transform. [Benchmark(Baseline = true)] [BenchmarkCategory("Forward8x8")] public int Forward8x8Scalar() { Av1Transform2dFlipConfiguration config = CreateForwardConfiguration(Av1TransformSize.Size8x8, 8); Av1ForwardTransformer.Transform2dScalar( this.spatial, this.coefficients, 8, ref config, this.workspace); return this.coefficients[63]; } /// /// Measures the Vector128 eight-by-eight forward DCT traversal. /// /// The last coefficient written by the transform. [Benchmark] [BenchmarkCategory("Forward8x8")] public int Forward8x8Vector128() { Av1Transform2dFlipConfiguration config = CreateForwardConfiguration(Av1TransformSize.Size8x8, 8); Av1ForwardTransformer.Transform2dVector128( this.spatial, this.coefficients, 8, ref config, this.workspace); return this.coefficients[63]; } /// /// Measures the Vector256 eight-by-eight forward DCT traversal. /// /// The last coefficient written by the transform. [Benchmark] [BenchmarkCategory("Forward8x8")] public int Forward8x8Vector256() { Av1Transform2dFlipConfiguration config = CreateForwardConfiguration(Av1TransformSize.Size8x8, 8); Av1ForwardTransformer.Transform2dVector256( this.spatial, this.coefficients, 8, ref config, this.workspace); return this.coefficients[63]; } /// /// Measures runtime dispatch of an eight-by-eight forward DCT block. /// /// The last coefficient written by the transform. [Benchmark] [BenchmarkCategory("Forward8x8")] public int Forward8x8Dispatch() { Av1ForwardTransformer.Transform2d( this.spatial, this.coefficients, 8, Av1TransformType.DctDct, Av1TransformSize.Size8x8, 8, this.workspace); return this.coefficients[63]; } /// /// Measures the scalar thirty-two-by-thirty-two forward DCT traversal. /// /// The last coefficient written by the transform. [Benchmark(Baseline = true)] [BenchmarkCategory("Forward32x32")] public int Forward32x32Scalar() { Av1Transform2dFlipConfiguration config = CreateForwardConfiguration(Av1TransformSize.Size32x32, 10); Av1ForwardTransformer.Transform2dScalar( this.spatial, this.coefficients, 32, ref config, this.workspace); return this.coefficients[^1]; } /// /// Measures the Vector128 thirty-two-by-thirty-two forward DCT traversal. /// /// The last coefficient written by the transform. [Benchmark] [BenchmarkCategory("Forward32x32")] public int Forward32x32Vector128() { Av1Transform2dFlipConfiguration config = CreateForwardConfiguration(Av1TransformSize.Size32x32, 10); Av1ForwardTransformer.Transform2dVector128( this.spatial, this.coefficients, 32, ref config, this.workspace); return this.coefficients[^1]; } /// /// Measures the Vector256 thirty-two-by-thirty-two forward DCT traversal. /// /// The last coefficient written by the transform. [Benchmark] [BenchmarkCategory("Forward32x32")] public int Forward32x32Vector256() { Av1Transform2dFlipConfiguration config = CreateForwardConfiguration(Av1TransformSize.Size32x32, 10); Av1ForwardTransformer.Transform2dVector256( this.spatial, this.coefficients, 32, ref config, this.workspace); return this.coefficients[^1]; } /// /// Measures the Vector512 thirty-two-by-thirty-two forward DCT traversal. /// /// The last coefficient written by the transform. [Benchmark] [BenchmarkCategory("Forward32x32")] public int Forward32x32Vector512() { Av1Transform2dFlipConfiguration config = CreateForwardConfiguration(Av1TransformSize.Size32x32, 10); Av1ForwardTransformer.Transform2dVector512( this.spatial, this.coefficients, 32, ref config, this.workspace); return this.coefficients[^1]; } /// /// Measures runtime dispatch of a thirty-two-by-thirty-two forward DCT block. /// /// The last coefficient written by the transform. [Benchmark] [BenchmarkCategory("Forward32x32")] public int Forward32x32Dispatch() { Av1ForwardTransformer.Transform2d( this.spatial, this.coefficients, 32, Av1TransformType.DctDct, Av1TransformSize.Size32x32, 10, this.workspace); return this.coefficients[^1]; } /// /// Measures the scalar eight-by-eight inverse DCT and byte reconstruction traversal. /// /// The last reconstructed sample. [Benchmark(Baseline = true)] [BenchmarkCategory("Inverse8x8")] public byte Inverse8x8Scalar() { Av1Transform2dFlipConfiguration config = CreateInverseConfiguration(Av1TransformSize.Size8x8, 8); Av1Inverse2dTransformer.Transform2dScalar( this.coefficients, this.prediction, 8, this.reconstruction, 8, ref config, this.workspace, 8); return this.reconstruction[63]; } /// /// Measures the Vector128 eight-by-eight inverse DCT and byte reconstruction traversal. /// /// The last reconstructed sample. [Benchmark] [BenchmarkCategory("Inverse8x8")] public byte Inverse8x8Vector128() { Av1Transform2dFlipConfiguration config = CreateInverseConfiguration(Av1TransformSize.Size8x8, 8); Av1Inverse2dTransformer.Transform2dVector128( this.coefficients, this.prediction, 8, this.reconstruction, 8, ref config, this.workspace, 8); return this.reconstruction[63]; } /// /// Measures the Vector256 eight-by-eight inverse DCT and byte reconstruction traversal. /// /// The last reconstructed sample. [Benchmark] [BenchmarkCategory("Inverse8x8")] public byte Inverse8x8Vector256() { Av1Transform2dFlipConfiguration config = CreateInverseConfiguration(Av1TransformSize.Size8x8, 8); Av1Inverse2dTransformer.Transform2dVector256( this.coefficients, this.prediction, 8, this.reconstruction, 8, ref config, this.workspace, 8); return this.reconstruction[63]; } /// /// Measures runtime dispatch of an eight-by-eight inverse DCT and byte reconstruction block. /// /// The last reconstructed sample. [Benchmark] [BenchmarkCategory("Inverse8x8")] public byte Inverse8x8Dispatch() { Av1InverseTransformer.Reconstruct8Bit( this.coefficients, this.prediction, 8, this.reconstruction, 8, Av1TransformSize.Size8x8, Av1TransformType.DctDct, 0, 64, false, this.workspace); return this.reconstruction[63]; } /// /// Measures the scalar thirty-two-by-thirty-two inverse DCT and byte reconstruction traversal. /// /// The last reconstructed sample. [Benchmark(Baseline = true)] [BenchmarkCategory("Inverse32x32")] public byte Inverse32x32Scalar() { Av1Transform2dFlipConfiguration config = CreateInverseConfiguration(Av1TransformSize.Size32x32, 8); Av1Inverse2dTransformer.Transform2dScalar( this.coefficients, this.prediction, 32, this.reconstruction, 32, ref config, this.workspace, 8); return this.reconstruction[^1]; } /// /// Measures the Vector128 thirty-two-by-thirty-two inverse DCT and byte reconstruction traversal. /// /// The last reconstructed sample. [Benchmark] [BenchmarkCategory("Inverse32x32")] public byte Inverse32x32Vector128() { Av1Transform2dFlipConfiguration config = CreateInverseConfiguration(Av1TransformSize.Size32x32, 8); Av1Inverse2dTransformer.Transform2dVector128( this.coefficients, this.prediction, 32, this.reconstruction, 32, ref config, this.workspace, 8); return this.reconstruction[^1]; } /// /// Measures the Vector256 thirty-two-by-thirty-two inverse DCT and byte reconstruction traversal. /// /// The last reconstructed sample. [Benchmark] [BenchmarkCategory("Inverse32x32")] public byte Inverse32x32Vector256() { Av1Transform2dFlipConfiguration config = CreateInverseConfiguration(Av1TransformSize.Size32x32, 8); Av1Inverse2dTransformer.Transform2dVector256( this.coefficients, this.prediction, 32, this.reconstruction, 32, ref config, this.workspace, 8); return this.reconstruction[^1]; } /// /// Measures the Vector512 thirty-two-by-thirty-two inverse DCT and byte reconstruction traversal. /// /// The last reconstructed sample. [Benchmark] [BenchmarkCategory("Inverse32x32")] public byte Inverse32x32Vector512() { Av1Transform2dFlipConfiguration config = CreateInverseConfiguration(Av1TransformSize.Size32x32, 8); Av1Inverse2dTransformer.Transform2dVector512( this.coefficients, this.prediction, 32, this.reconstruction, 32, ref config, this.workspace, 8); return this.reconstruction[^1]; } /// /// Measures runtime dispatch of a thirty-two-by-thirty-two inverse DCT and byte reconstruction block. /// /// The last reconstructed sample. [Benchmark] [BenchmarkCategory("Inverse32x32")] public byte Inverse32x32Dispatch() { Av1InverseTransformer.Reconstruct8Bit( this.coefficients, this.prediction, 32, this.reconstruction, 32, Av1TransformSize.Size32x32, Av1TransformType.DctDct, 0, 1024, false, this.workspace); return this.reconstruction[^1]; } /// /// Creates a forward DCT configuration with the normative stage ranges for one coded bit depth. /// /// The dimensions of the transform block. /// The coded sample bit depth. /// The initialized transform configuration. private static Av1Transform2dFlipConfiguration CreateForwardConfiguration(Av1TransformSize transformSize, int bitDepth) => Av1Transform2dFlipConfiguration.CreateForward(Av1TransformType.DctDct, transformSize, bitDepth); /// /// Creates an inverse DCT configuration with the normative stage ranges for one coded bit depth. /// /// The dimensions of the transform block. /// The coded sample bit depth. /// The initialized transform configuration. private static Av1Transform2dFlipConfiguration CreateInverseConfiguration(Av1TransformSize transformSize, int bitDepth) => Av1Transform2dFlipConfiguration.CreateInverse(Av1TransformType.DctDct, transformSize, bitDepth); }