📷 A modern, cross-platform, 2D Graphics library for .NET
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

160 lines
4.9 KiB

// Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0.
using System.Numerics;
using System.Runtime.CompilerServices;
using BenchmarkDotNet.Attributes;
using SixLabors.ImageSharp.Formats.Jpeg.Components;
// ReSharper disable InconsistentNaming
namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg.BlockOperations
{
/// <summary>
/// The goal of this benchmark is to measure the following Jpeg-related scenario:
/// - Take 2 blocks of float-s
/// - Divide each float pair, round the result
/// - Iterate through all rounded values as int-s
/// </summary>
public unsafe class Block8x8F_DivideRound
{
private const int ExecutionCount = 5; // Added this to reduce the effect of copying the blocks
private static readonly Vector4 MinusOne = new Vector4(-1);
private static readonly Vector4 Half = new Vector4(0.5f);
private Block8x8F inputDividend;
private Block8x8F inputDivisor;
[GlobalSetup]
public void Setup()
{
for (int i = 0; i < Block8x8F.Size; i++)
{
this.inputDividend[i] = i * 44.8f;
this.inputDivisor[i] = 100 - i;
}
}
[Benchmark(Baseline = true)]
public int ByRationalIntegers()
{
int sum = 0;
Block8x8F b1 = this.inputDividend;
Block8x8F b2 = this.inputDivisor;
float* pDividend = (float*)&b1;
float* pDivisor = (float*)&b2;
int* result = stackalloc int[Block8x8F.Size];
for (int cnt = 0; cnt < ExecutionCount; cnt++)
{
sum = 0;
for (int i = 0; i < Block8x8F.Size; i++)
{
int a = (int)pDividend[i];
int b = (int)pDivisor;
result[i] = RationalRound(a, b);
}
for (int i = 0; i < Block8x8F.Size; i++)
{
sum += result[i];
}
}
return sum;
}
[Benchmark]
public int BySystemMathRound()
{
int sum = 0;
Block8x8F b1 = this.inputDividend;
Block8x8F b2 = this.inputDivisor;
float* pDividend = (float*)&b1;
float* pDivisor = (float*)&b2;
for (int cnt = 0; cnt < ExecutionCount; cnt++)
{
sum = 0;
for (int i = 0; i < Block8x8F.Size; i++)
{
double value = pDividend[i] / pDivisor[i];
pDividend[i] = (float)System.Math.Round(value);
}
for (int i = 0; i < Block8x8F.Size; i++)
{
sum += (int)pDividend[i];
}
}
return sum;
}
[Benchmark]
public int BySimdMagic()
{
int sum = 0;
Block8x8F bDividend = this.inputDividend;
Block8x8F bDivisor = this.inputDivisor;
float* pDividend = (float*)&bDividend;
for (int cnt = 0; cnt < ExecutionCount; cnt++)
{
sum = 0;
DivideRoundAll(ref bDividend, ref bDivisor);
for (int i = 0; i < Block8x8F.Size; i++)
{
sum += (int)pDividend[i];
}
}
return sum;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static void DivideRoundAll(ref Block8x8F a, ref Block8x8F b)
{
a.V0L = DivideRound(a.V0L, b.V0L);
a.V0R = DivideRound(a.V0R, b.V0R);
a.V1L = DivideRound(a.V1L, b.V1L);
a.V1R = DivideRound(a.V1R, b.V1R);
a.V2L = DivideRound(a.V2L, b.V2L);
a.V2R = DivideRound(a.V2R, b.V2R);
a.V3L = DivideRound(a.V3L, b.V3L);
a.V3R = DivideRound(a.V3R, b.V3R);
a.V4L = DivideRound(a.V4L, b.V4L);
a.V4R = DivideRound(a.V4R, b.V4R);
a.V5L = DivideRound(a.V5L, b.V5L);
a.V5R = DivideRound(a.V5R, b.V5R);
a.V6L = DivideRound(a.V6L, b.V6L);
a.V6R = DivideRound(a.V6R, b.V6R);
a.V7L = DivideRound(a.V7L, b.V7L);
a.V7R = DivideRound(a.V7R, b.V7R);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static Vector4 DivideRound(Vector4 dividend, Vector4 divisor)
{
var sign = Vector4.Min(dividend, Vector4.One);
sign = Vector4.Max(sign, MinusOne);
return (dividend / divisor) + (sign * Half);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static int RationalRound(int dividend, int divisor)
{
if (dividend >= 0)
{
return (dividend + (divisor >> 1)) / divisor;
}
return -((-dividend + (divisor >> 1)) / divisor);
}
}
}