Improved performance of Average on Images

This commit is contained in:
Darth Affe 2024-08-19 22:41:56 +02:00
parent f4c1eaddd6
commit e6b77ecfdb
12 changed files with 84 additions and 98 deletions

View File

@ -13,6 +13,8 @@ public class AverageBenchmarks
private readonly List<ColorRGB[]> _colors3bpp; private readonly List<ColorRGB[]> _colors3bpp;
private readonly List<ColorRGBA[]> _colors4bpp; private readonly List<ColorRGBA[]> _colors4bpp;
private readonly List<IImage<ColorRGB>> _images3bpp;
private readonly List<IImage<ColorRGBA>> _images4bpp;
#endregion #endregion
@ -22,6 +24,9 @@ public class AverageBenchmarks
{ {
_colors3bpp = BenchmarkHelper.GetSampleData<ColorRGB>(); _colors3bpp = BenchmarkHelper.GetSampleData<ColorRGB>();
_colors4bpp = BenchmarkHelper.GetSampleData<ColorRGBA>(); _colors4bpp = BenchmarkHelper.GetSampleData<ColorRGBA>();
_images3bpp = BenchmarkHelper.GetSampleDataImages<ColorRGB>();
_images4bpp = BenchmarkHelper.GetSampleDataImages<ColorRGBA>();
} }
#endregion #endregion
@ -48,6 +53,26 @@ public class AverageBenchmarks
return averages; return averages;
} }
[Benchmark]
public ColorRGB[] PixelHelper_3BPP_Image()
{
ColorRGB[] averages = new ColorRGB[_images3bpp.Count];
for (int i = 0; i < _images3bpp.Count; i++)
averages[i] = _images3bpp[i].Average();
return averages;
}
[Benchmark]
public ColorRGBA[] PixelHelper_4BPP_Image()
{
ColorRGBA[] averages = new ColorRGBA[_images4bpp.Count];
for (int i = 0; i < _images4bpp.Count; i++)
averages[i] = _images4bpp[i].Average();
return averages;
}
[Benchmark] [Benchmark]
public ColorRGB[] Reference_3BPP() public ColorRGB[] Reference_3BPP()
{ {

View File

@ -19,7 +19,6 @@ public class ColorSourceGenerator : IIncrementalGenerator
private static readonly IGeneratorFeature[] FEATURES = private static readonly IGeneratorFeature[] FEATURES =
[ [
new Colors(), new Colors(),
new Average(),
new MinMax(), new MinMax(),
new Sum(), new Sum(),
new Quantize() new Quantize()

View File

@ -1,51 +0,0 @@
using System.Collections.Generic;
using System.Collections.Immutable;
namespace HPPH.Generators;
internal class Average : IGeneratorFeature
{
public IEnumerable<(string name, string source)> GenerateFor(ColorFormatData colorFormat)
{
yield return ($"ColorFormat{colorFormat.Format}.Average", GenerateColorFormatAverage(colorFormat));
}
public IEnumerable<(string name, string source)> GenerateFor(ImmutableArray<ColorFormatData> colorFormats)
{
yield return ("IColorFormat.Average", GenerateColorFormatInterfaceAverage());
}
private static string GenerateColorFormatAverage(ColorFormatData colorFormat)
{
return $$"""
#nullable enable
using System.Runtime.InteropServices;
namespace HPPH;
public sealed partial class ColorFormat{{colorFormat.Format}}
{
#region Methods
unsafe IColor IColorFormat.Average(ReadOnlySpan<byte> data) => PixelHelper.Average(MemoryMarshal.Cast<byte, Color{{colorFormat.Format}}>(data));
#endregion
}
""";
}
private static string GenerateColorFormatInterfaceAverage()
{
return """
#nullable enable
namespace HPPH;
public partial interface IColorFormat
{
internal IColor Average(ReadOnlySpan<byte> data);
}
""";
}
}

View File

@ -218,6 +218,12 @@ internal class Colors : IGeneratorFeature
private ColorFormat{{colorFormat.Format}}() {} private ColorFormat{{colorFormat.Format}}() {}
#endregion #endregion
#region Methods
public IColor CreateColor(byte r, byte g, byte b, byte a) => Color{{colorFormat.Format}}.Create(r, g, b, a);
#endregion
} }
"""; """;
} }
@ -243,6 +249,8 @@ internal class Colors : IGeneratorFeature
sb.AppendLine(); sb.AppendLine();
sb.AppendLine(""" sb.AppendLine("""
IColor CreateColor(byte r, byte g, byte b, byte a);
#endregion #endregion
} }
"""); """);

View File

@ -21,4 +21,10 @@ public sealed partial class ColorFormatABGR : IColorFormat
private ColorFormatABGR() {} private ColorFormatABGR() {}
#endregion #endregion
#region Methods
public IColor CreateColor(byte r, byte g, byte b, byte a) => ColorABGR.Create(r, g, b, a);
#endregion
} }

View File

@ -21,4 +21,10 @@ public sealed partial class ColorFormatARGB : IColorFormat
private ColorFormatARGB() {} private ColorFormatARGB() {}
#endregion #endregion
#region Methods
public IColor CreateColor(byte r, byte g, byte b, byte a) => ColorARGB.Create(r, g, b, a);
#endregion
} }

View File

@ -21,4 +21,10 @@ public sealed partial class ColorFormatBGR : IColorFormat
private ColorFormatBGR() {} private ColorFormatBGR() {}
#endregion #endregion
#region Methods
public IColor CreateColor(byte r, byte g, byte b, byte a) => ColorBGR.Create(r, g, b, a);
#endregion
} }

View File

@ -21,4 +21,10 @@ public sealed partial class ColorFormatBGRA : IColorFormat
private ColorFormatBGRA() {} private ColorFormatBGRA() {}
#endregion #endregion
#region Methods
public IColor CreateColor(byte r, byte g, byte b, byte a) => ColorBGRA.Create(r, g, b, a);
#endregion
} }

View File

@ -21,4 +21,10 @@ public sealed partial class ColorFormatRGB : IColorFormat
private ColorFormatRGB() {} private ColorFormatRGB() {}
#endregion #endregion
#region Methods
public IColor CreateColor(byte r, byte g, byte b, byte a) => ColorRGB.Create(r, g, b, a);
#endregion
} }

View File

@ -21,4 +21,10 @@ public sealed partial class ColorFormatRGBA : IColorFormat
private ColorFormatRGBA() {} private ColorFormatRGBA() {}
#endregion #endregion
#region Methods
public IColor CreateColor(byte r, byte g, byte b, byte a) => ColorRGBA.Create(r, g, b, a);
#endregion
} }

View File

@ -13,5 +13,7 @@ public partial interface IColorFormat
public static ColorFormatRGBA RGBA => ColorFormatRGBA.Instance; public static ColorFormatRGBA RGBA => ColorFormatRGBA.Instance;
public static ColorFormatBGRA BGRA => ColorFormatBGRA.Instance; public static ColorFormatBGRA BGRA => ColorFormatBGRA.Instance;
IColor CreateColor(byte r, byte g, byte b, byte a);
#endregion #endregion
} }

View File

@ -1,5 +1,4 @@
using System.Buffers; using System.Runtime.CompilerServices;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
namespace HPPH; namespace HPPH;
@ -12,29 +11,14 @@ public static partial class PixelHelper
{ {
ArgumentNullException.ThrowIfNull(image); ArgumentNullException.ThrowIfNull(image);
int dataLength = image.SizeInBytes; float count = image.Width * image.Height;
if (dataLength <= 1024) ISum sum = Sum(image);
{
Span<byte> buffer = stackalloc byte[dataLength];
image.CopyTo(buffer); return image.ColorFormat.CreateColor((byte)MathF.Round(sum.R / count),
return image.ColorFormat.Average(buffer); (byte)MathF.Round(sum.G / count),
} (byte)MathF.Round(sum.B / count),
else (byte)MathF.Round(sum.A / count));
{
byte[] array = ArrayPool<byte>.Shared.Rent(dataLength);
Span<byte> buffer = array.AsSpan()[..dataLength];
try
{
image.CopyTo(buffer);
return image.ColorFormat.Average(buffer);
}
finally
{
ArrayPool<byte>.Shared.Return(array);
}
}
} }
public static T Average<T>(this IImage<T> image) public static T Average<T>(this IImage<T> image)
@ -48,30 +32,13 @@ public static partial class PixelHelper
public static T Average<T>(this RefImage<T> image) public static T Average<T>(this RefImage<T> image)
where T : struct, IColor where T : struct, IColor
{ {
int dataLength = image.Width * image.Height; float count = image.Width * image.Height;
int sizeInBytes = dataLength * T.ColorFormat.BytesPerPixel;
if (sizeInBytes <= 1024) ISum sum = Sum(image);
{ return (T)T.Create((byte)MathF.Round(sum.R / count),
Span<T> buffer = MemoryMarshal.Cast<byte, T>(stackalloc byte[sizeInBytes]); (byte)MathF.Round(sum.G / count),
(byte)MathF.Round(sum.B / count),
image.CopyTo(buffer); (byte)MathF.Round(sum.A / count));
return Average(buffer);
}
else
{
T[] array = ArrayPool<T>.Shared.Rent(dataLength);
Span<T> buffer = array.AsSpan()[..dataLength];
try
{
image.CopyTo(buffer);
return Average(buffer);
}
finally
{
ArrayPool<T>.Shared.Return(array);
}
}
} }
public static T Average<T>(this Span<T> colors) public static T Average<T>(this Span<T> colors)