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 unsafe string ConvertToString(ReadOnlySpan<byte> buffer)
private string ConvertToString(ReadOnlySpan<byte> buffer)
{
int nullCharIndex = buffer.IndexOf((byte)0);
@ -382,13 +382,13 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
this.invalidTags.Add(tag);
}
[MethodImpl(InliningOptions.ShortMethod)]
private TEnum ToEnum<TEnum>(int value, TEnum defaultValue)
where TEnum : struct
{
var enumValue = (TEnum)(object)value;
if (Enum.GetValues(typeof(TEnum)).Cast<TEnum>().Any(v => v.Equals(enumValue)))
if (EnumHelper<TEnum>.IsDefined(value))
{
return enumValue;
return Unsafe.As<int, TEnum>(ref value);
}
return defaultValue;
@ -557,5 +557,18 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
? BinaryPrimitives.ReadInt16BigEndian(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.Runtime.CompilerServices;
@ -6,6 +9,7 @@ using BenchmarkDotNet.Attributes;
using SixLabors.ImageSharp.Formats.Jpeg.Components;
using SixLabors.ImageSharp.Memory;
// ReSharper disable InconsistentNaming
namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg.BlockOperations
{
@ -22,7 +26,7 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg.BlockOperations
{
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);
@ -58,7 +62,17 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg.BlockOperations
public void UseVector8()
{
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> 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> row7 = Unsafe.As<Vector4, Vector<float>>(ref s.V7L);
d = row0;
Unsafe.Add(ref d, 1) = row1;
Unsafe.Add(ref d, 2) = row2;
Unsafe.Add(ref d, 3) = row3;
Unsafe.Add(ref d, 4) = row4;
Unsafe.Add(ref d, 5) = row5;
Unsafe.Add(ref d, 6) = row6;
Unsafe.Add(ref d, 7) = row7;
d0 = row0;
d1 = row1;
d2 = row2;
d3 = row3;
d4 = row4;
d5 = row5;
d6 = row6;
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.Memory;
using SixLabors.ImageSharp.Tests.Formats.Jpg.Utils;
using SixLabors.Memory;
using SixLabors.Primitives;
using Xunit;
@ -71,7 +72,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
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);
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,
};
//[Theory] // Benchmark, enable manually
//[MemberData(nameof(DecodeJpegData))]
[Theory] // Benchmark, enable manually
[MemberData(nameof(DecodeJpegData))]
public void DecodeJpeg(string fileName)
{
this.DecodeJpegBenchmarkImpl(fileName, new JpegDecoder());
@ -62,6 +62,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
() =>
{
var img = Image.Load<Rgba32>(bytes, decoder);
img.Dispose();
},
// ReSharper disable once ExplicitCallerInfoArgument
$"Decode {fileName}");

Loading…
Cancel
Save