Browse Source

optimize ExifReader.ToEnum<TEnum>(...)

pull/768/head
Anton Firszov 8 years ago
parent
commit
c34e4ff85f
  1. 21
      src/ImageSharp/MetaData/Profiles/Exif/ExifReader.cs
  2. 73
      tests/ImageSharp.Benchmarks/Codecs/Jpeg/BlockOperations/Block8x8F_CopyTo1x1.cs
  3. 3
      tests/ImageSharp.Tests/Formats/Jpg/Block8x8FTests.CopyToBufferArea.cs
  4. 5
      tests/ImageSharp.Tests/Formats/Jpg/JpegProfilingBenchmarks.cs

21
src/ImageSharp/MetaData/Profiles/Exif/ExifReader.cs

@ -124,7 +124,7 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
private byte ConvertToByte(ReadOnlySpan<byte> buffer) => buffer[0]; private byte ConvertToByte(ReadOnlySpan<byte> buffer) => buffer[0];
private unsafe string ConvertToString(ReadOnlySpan<byte> buffer) private string ConvertToString(ReadOnlySpan<byte> buffer)
{ {
int nullCharIndex = buffer.IndexOf((byte)0); int nullCharIndex = buffer.IndexOf((byte)0);
@ -382,13 +382,13 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
this.invalidTags.Add(tag); this.invalidTags.Add(tag);
} }
[MethodImpl(InliningOptions.ShortMethod)]
private TEnum ToEnum<TEnum>(int value, TEnum defaultValue) private TEnum ToEnum<TEnum>(int value, TEnum defaultValue)
where TEnum : struct where TEnum : struct
{ {
var enumValue = (TEnum)(object)value; if (EnumHelper<TEnum>.IsDefined(value))
if (Enum.GetValues(typeof(TEnum)).Cast<TEnum>().Any(v => v.Equals(enumValue)))
{ {
return enumValue; return Unsafe.As<int, TEnum>(ref value);
} }
return defaultValue; return defaultValue;
@ -557,5 +557,18 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
? BinaryPrimitives.ReadInt16BigEndian(buffer) ? BinaryPrimitives.ReadInt16BigEndian(buffer)
: BinaryPrimitives.ReadInt16LittleEndian(buffer); : BinaryPrimitives.ReadInt16LittleEndian(buffer);
} }
private class EnumHelper<TEnum>
where TEnum : struct
{
private static readonly int[] Values = Enum.GetValues(typeof(TEnum)).Cast<TEnum>()
.Select(e => Convert.ToInt32(e)).OrderBy(e => e).ToArray();
[MethodImpl(InliningOptions.ShortMethod)]
public static bool IsDefined(int value)
{
return Array.BinarySearch(Values, 0, Values.Length, value) > 0;
}
}
} }
} }

73
tests/ImageSharp.Benchmarks/Codecs/Jpeg/BlockOperations/Block8x8F_CopyTo1x1.cs

@ -1,4 +1,7 @@
using System; // Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
using System.Numerics; using System.Numerics;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
@ -6,6 +9,7 @@ using BenchmarkDotNet.Attributes;
using SixLabors.ImageSharp.Formats.Jpeg.Components; using SixLabors.ImageSharp.Formats.Jpeg.Components;
using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.Memory;
// ReSharper disable InconsistentNaming
namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg.BlockOperations namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg.BlockOperations
{ {
@ -22,7 +26,7 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg.BlockOperations
{ {
if (!SimdUtils.IsAvx2CompatibleArchitecture) if (!SimdUtils.IsAvx2CompatibleArchitecture)
{ {
throw new InvalidOperationException("Block8x8F_CopyTo1x1 is invalid on platforms without AVX2 support."); throw new InvalidOperationException("Benchmark Block8x8F_CopyTo1x1 is invalid on platforms without AVX2 support.");
} }
this.buffer = Configuration.Default.MemoryAllocator.Allocate2D<float>(1000, 500); this.buffer = Configuration.Default.MemoryAllocator.Allocate2D<float>(1000, 500);
@ -58,7 +62,17 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg.BlockOperations
public void UseVector8() public void UseVector8()
{ {
ref Block8x8F s = ref this.block; ref Block8x8F s = ref this.block;
ref Vector<float> d = ref Unsafe.As<float, Vector<float>>(ref this.destArea.GetReferenceToOrigin()); ref float origin = ref this.destArea.GetReferenceToOrigin();
int stride = this.destArea.Stride;
ref Vector<float> d0 = ref Unsafe.As<float, Vector<float>>(ref origin);
ref Vector<float> d1 = ref Unsafe.As<float, Vector<float>>(ref Unsafe.Add(ref origin, stride));
ref Vector<float> d2 = ref Unsafe.As<float, Vector<float>>(ref Unsafe.Add(ref origin, stride * 2));
ref Vector<float> d3 = ref Unsafe.As<float, Vector<float>>(ref Unsafe.Add(ref origin, stride * 3));
ref Vector<float> d4 = ref Unsafe.As<float, Vector<float>>(ref Unsafe.Add(ref origin, stride * 4));
ref Vector<float> d5 = ref Unsafe.As<float, Vector<float>>(ref Unsafe.Add(ref origin, stride * 5));
ref Vector<float> d6 = ref Unsafe.As<float, Vector<float>>(ref Unsafe.Add(ref origin, stride * 6));
ref Vector<float> d7 = ref Unsafe.As<float, Vector<float>>(ref Unsafe.Add(ref origin, stride * 7));
Vector<float> row0 = Unsafe.As<Vector4, Vector<float>>(ref s.V0L); Vector<float> row0 = Unsafe.As<Vector4, Vector<float>>(ref s.V0L);
Vector<float> row1 = Unsafe.As<Vector4, Vector<float>>(ref s.V1L); Vector<float> row1 = Unsafe.As<Vector4, Vector<float>>(ref s.V1L);
@ -69,14 +83,51 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg.BlockOperations
Vector<float> row6 = Unsafe.As<Vector4, Vector<float>>(ref s.V6L); Vector<float> row6 = Unsafe.As<Vector4, Vector<float>>(ref s.V6L);
Vector<float> row7 = Unsafe.As<Vector4, Vector<float>>(ref s.V7L); Vector<float> row7 = Unsafe.As<Vector4, Vector<float>>(ref s.V7L);
d = row0; d0 = row0;
Unsafe.Add(ref d, 1) = row1; d1 = row1;
Unsafe.Add(ref d, 2) = row2; d2 = row2;
Unsafe.Add(ref d, 3) = row3; d3 = row3;
Unsafe.Add(ref d, 4) = row4; d4 = row4;
Unsafe.Add(ref d, 5) = row5; d5 = row5;
Unsafe.Add(ref d, 6) = row6; d6 = row6;
Unsafe.Add(ref d, 7) = row7; d7 = row7;
}
[Benchmark]
public void UseVector8_V2()
{
ref Block8x8F s = ref this.block;
ref float origin = ref this.destArea.GetReferenceToOrigin();
int stride = this.destArea.Stride;
ref Vector<float> d0 = ref Unsafe.As<float, Vector<float>>(ref origin);
ref Vector<float> d1 = ref Unsafe.As<float, Vector<float>>(ref Unsafe.Add(ref origin, stride));
ref Vector<float> d2 = ref Unsafe.As<float, Vector<float>>(ref Unsafe.Add(ref origin, stride * 2));
ref Vector<float> d3 = ref Unsafe.As<float, Vector<float>>(ref Unsafe.Add(ref origin, stride * 3));
ref Vector<float> d4 = ref Unsafe.As<float, Vector<float>>(ref Unsafe.Add(ref origin, stride * 4));
ref Vector<float> d5 = ref Unsafe.As<float, Vector<float>>(ref Unsafe.Add(ref origin, stride * 5));
ref Vector<float> d6 = ref Unsafe.As<float, Vector<float>>(ref Unsafe.Add(ref origin, stride * 6));
ref Vector<float> d7 = ref Unsafe.As<float, Vector<float>>(ref Unsafe.Add(ref origin, stride * 7));
d0 = Unsafe.As<Vector4, Vector<float>>(ref s.V0L);
d1 = Unsafe.As<Vector4, Vector<float>>(ref s.V1L);
d2 = Unsafe.As<Vector4, Vector<float>>(ref s.V2L);
d3 = Unsafe.As<Vector4, Vector<float>>(ref s.V3L);
d4 = Unsafe.As<Vector4, Vector<float>>(ref s.V4L);
d5 = Unsafe.As<Vector4, Vector<float>>(ref s.V5L);
d6 = Unsafe.As<Vector4, Vector<float>>(ref s.V6L);
d7 = Unsafe.As<Vector4, Vector<float>>(ref s.V7L);
} }
// RESULTS:
//
// Method | Mean | Error | StdDev | Scaled |
// -------------- |---------:|----------:|----------:|-------:|
// Original | 22.53 ns | 0.1660 ns | 0.1553 ns | 1.00 |
// UseVector8 | 21.59 ns | 0.3079 ns | 0.2571 ns | 0.96 |
// UseVector8_V2 | 22.57 ns | 0.1699 ns | 0.1506 ns | 1.00 |
//
// Conclusion:
// Doesn't worth to bother with this
} }
} }

3
tests/ImageSharp.Tests/Formats/Jpg/Block8x8FTests.CopyToBufferArea.cs

@ -7,6 +7,7 @@
using SixLabors.ImageSharp.Formats.Jpeg.Components; using SixLabors.ImageSharp.Formats.Jpeg.Components;
using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.Tests.Formats.Jpg.Utils; using SixLabors.ImageSharp.Tests.Formats.Jpg.Utils;
using SixLabors.Memory;
using SixLabors.Primitives; using SixLabors.Primitives;
using Xunit; using Xunit;
@ -71,7 +72,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
var start = new Point(50, 50); var start = new Point(50, 50);
using (Buffer2D<float> buffer = Configuration.Default.MemoryAllocator.Allocate2D<float>(100, 100)) using (Buffer2D<float> buffer = Configuration.Default.MemoryAllocator.Allocate2D<float>(100, 100, AllocationOptions.Clean))
{ {
BufferArea<float> area = buffer.GetArea(start.X, start.Y, 8 * horizontalFactor, 8 * verticalFactor); BufferArea<float> area = buffer.GetArea(start.X, start.Y, 8 * horizontalFactor, 8 * verticalFactor);
block.CopyTo(area, horizontalFactor, verticalFactor); block.CopyTo(area, horizontalFactor, verticalFactor);

5
tests/ImageSharp.Tests/Formats/Jpg/JpegProfilingBenchmarks.cs

@ -32,8 +32,8 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
TestImages.Jpeg.Baseline.Jpeg444, TestImages.Jpeg.Baseline.Jpeg444,
}; };
//[Theory] // Benchmark, enable manually [Theory] // Benchmark, enable manually
//[MemberData(nameof(DecodeJpegData))] [MemberData(nameof(DecodeJpegData))]
public void DecodeJpeg(string fileName) public void DecodeJpeg(string fileName)
{ {
this.DecodeJpegBenchmarkImpl(fileName, new JpegDecoder()); this.DecodeJpegBenchmarkImpl(fileName, new JpegDecoder());
@ -62,6 +62,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
() => () =>
{ {
var img = Image.Load<Rgba32>(bytes, decoder); var img = Image.Load<Rgba32>(bytes, decoder);
img.Dispose();
}, },
// ReSharper disable once ExplicitCallerInfoArgument // ReSharper disable once ExplicitCallerInfoArgument
$"Decode {fileName}"); $"Decode {fileName}");

Loading…
Cancel
Save