Browse Source

cold/hot path microoptimizations

based on Resize profile
pull/1143/head
Anton Firszov 6 years ago
parent
commit
48d3963cdd
  1. 9
      src/ImageSharp/Memory/Allocators/ArrayPoolMemoryAllocator.Buffer{T}.cs
  2. 8
      src/ImageSharp/Memory/Allocators/ArrayPoolMemoryAllocator.cs
  3. 46
      src/ImageSharp/Memory/Buffer2DExtensions.cs
  4. 46
      src/ImageSharp/Memory/Buffer2D{T}.cs
  5. 18
      src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeKernelMap.cs

9
src/ImageSharp/Memory/Allocators/ArrayPoolMemoryAllocator.Buffer{T}.cs

@ -3,6 +3,7 @@
using System;
using System.Buffers;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using SixLabors.ImageSharp.Memory.Internals;
@ -50,7 +51,7 @@ namespace SixLabors.ImageSharp.Memory
{
if (this.Data == null)
{
throw new ObjectDisposedException("ArrayPoolMemoryAllocator.Buffer<T>");
ThrowObjectDisposedException();
}
return MemoryMarshal.Cast<byte, T>(this.Data.AsSpan()).Slice(0, this.length);
@ -74,6 +75,12 @@ namespace SixLabors.ImageSharp.Memory
}
protected override object GetPinnableObject() => this.Data;
[MethodImpl(MethodImplOptions.NoInlining)]
private static void ThrowObjectDisposedException()
{
throw new ObjectDisposedException("ArrayPoolMemoryAllocator.Buffer<T>");
}
}
/// <summary>

8
src/ImageSharp/Memory/Allocators/ArrayPoolMemoryAllocator.cs

@ -133,8 +133,7 @@ namespace SixLabors.ImageSharp.Memory
int bufferSizeInBytes = length * itemSizeBytes;
if (bufferSizeInBytes < 0 || bufferSizeInBytes > this.BufferCapacityInBytes)
{
throw new InvalidMemoryOperationException(
$"Requested allocation: {length} elements of {typeof(T).Name} is over the capacity of the MemoryAllocator.");
ThrowInvalidAllocationException<T>(length);
}
ArrayPool<byte> pool = this.GetArrayPool(bufferSizeInBytes);
@ -171,6 +170,11 @@ namespace SixLabors.ImageSharp.Memory
return maxPoolSizeInBytes / 4;
}
[MethodImpl(InliningOptions.ColdPath)]
private static void ThrowInvalidAllocationException<T>(int length) =>
throw new InvalidMemoryOperationException(
$"Requested allocation: {length} elements of {typeof(T).Name} is over the capacity of the MemoryAllocator.");
private ArrayPool<byte> GetArrayPool(int bufferSizeInBytes)
{
return bufferSizeInBytes <= this.PoolSelectorThresholdInBytes ? this.normalArrayPool : this.largeArrayPool;

46
src/ImageSharp/Memory/Buffer2DExtensions.cs

@ -27,52 +27,6 @@ namespace SixLabors.ImageSharp.Memory
return buffer.FastMemoryGroup.View;
}
/// <summary>
/// Gets a <see cref="Span{T}"/> to the backing data of <paramref name="buffer"/>
/// if the backing group consists of one single contiguous memory buffer.
/// Throws <see cref="InvalidOperationException"/> otherwise.
/// </summary>
/// <param name="buffer">The <see cref="Buffer2D{T}"/>.</param>
/// <typeparam name="T">The value type.</typeparam>
/// <returns>The <see cref="Span{T}"/> referencing the memory area.</returns>
/// <exception cref="InvalidOperationException">
/// Thrown when the backing group is discontiguous.
/// </exception>
internal static Span<T> GetSingleSpan<T>(this Buffer2D<T> buffer)
where T : struct
{
Guard.NotNull(buffer, nameof(buffer));
if (buffer.FastMemoryGroup.Count > 1)
{
throw new InvalidOperationException("GetSingleSpan is only valid for a single-buffer group!");
}
return buffer.FastMemoryGroup.Single().Span;
}
/// <summary>
/// Gets a <see cref="Memory{T}"/> to the backing data of <paramref name="buffer"/>
/// if the backing group consists of one single contiguous memory buffer.
/// Throws <see cref="InvalidOperationException"/> otherwise.
/// </summary>
/// <param name="buffer">The <see cref="Buffer2D{T}"/>.</param>
/// <typeparam name="T">The value type.</typeparam>
/// <returns>The <see cref="Memory{T}"/>.</returns>
/// <exception cref="InvalidOperationException">
/// Thrown when the backing group is discontiguous.
/// </exception>
internal static Memory<T> GetSingleMemory<T>(this Buffer2D<T> buffer)
where T : struct
{
Guard.NotNull(buffer, nameof(buffer));
if (buffer.FastMemoryGroup.Count > 1)
{
throw new InvalidOperationException("GetSingleMemory is only valid for a single-buffer group!");
}
return buffer.FastMemoryGroup.Single();
}
/// <summary>
/// TODO: Does not work with multi-buffer groups, should be specific to Resize.
/// Copy <paramref name="columnCount"/> columns of <paramref name="buffer"/> inplace,

46
src/ImageSharp/Memory/Buffer2D{T}.cs

@ -2,6 +2,7 @@
// Licensed under the Apache License, Version 2.0.
using System;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
@ -158,6 +159,36 @@ namespace SixLabors.ImageSharp.Memory
return this.FastMemoryGroup.View.GetBoundedSlice(y * this.Width, this.Width);
}
/// <summary>
/// Gets a <see cref="Span{T}"/> to the backing data if the backing group consists of a single contiguous memory buffer.
/// Throws <see cref="InvalidOperationException"/> otherwise.
/// </summary>
/// <returns>The <see cref="Span{T}"/> referencing the memory area.</returns>
/// <exception cref="InvalidOperationException">
/// Thrown when the backing group is discontiguous.
/// </exception>
[MethodImpl(InliningOptions.ShortMethod)]
internal Span<T> GetSingleSpan()
{
// TODO: If we need a public version of this method, we need to cache the non-fast Memory<T> of this.MemoryGroup
return this.cachedMemory.Length != 0 ? this.cachedMemory.Span : ThrowInvalidOperationSingleSpan();
}
/// <summary>
/// Gets a <see cref="Memory{T}"/> to the backing data of if the backing group consists of a single contiguous memory buffer.
/// Throws <see cref="InvalidOperationException"/> otherwise.
/// </summary>
/// <returns>The <see cref="Memory{T}"/>.</returns>
/// <exception cref="InvalidOperationException">
/// Thrown when the backing group is discontiguous.
/// </exception>
[MethodImpl(InliningOptions.ShortMethod)]
internal Memory<T> GetSingleMemory()
{
// TODO: If we need a public version of this method, we need to cache the non-fast Memory<T> of this.MemoryGroup
return this.cachedMemory.Length != 0 ? this.cachedMemory : ThrowInvalidOperationSingleMemory();
}
/// <summary>
/// Swaps the contents of 'destination' with 'source' if the buffers are owned (1),
/// copies the contents of 'source' to 'destination' otherwise (2). Buffers should be of same size in case 2!
@ -171,6 +202,9 @@ namespace SixLabors.ImageSharp.Memory
[MethodImpl(InliningOptions.ColdPath)]
private Memory<T> GetRowMemorySlow(int y) => this.FastMemoryGroup.GetBoundedSlice(y * this.Width, this.Width);
[MethodImpl(InliningOptions.ColdPath)]
private Memory<T> GetSingleMemorySlow() => this.FastMemoryGroup.Single();
[MethodImpl(InliningOptions.ColdPath)]
private ref T GetElementSlow(int x, int y)
{
@ -196,5 +230,17 @@ namespace SixLabors.ImageSharp.Memory
b.cachedMemory = aCached;
}
}
[MethodImpl(InliningOptions.ColdPath)]
private static Memory<T> ThrowInvalidOperationSingleMemory()
{
throw new InvalidOperationException("GetSingleMemory is only valid for a single-buffer group!");
}
[MethodImpl(InliningOptions.ColdPath)]
private static Span<T> ThrowInvalidOperationSingleSpan()
{
throw new InvalidOperationException("GetSingleSpan is only valid for a single-buffer group!");
}
}
}

18
src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeKernelMap.cs

@ -3,6 +3,7 @@
using System;
using System.Buffers;
using System.Diagnostics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using SixLabors.ImageSharp.Memory;
@ -249,12 +250,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
private unsafe ResizeKernel CreateKernel(int dataRowIndex, int left, int right)
{
int length = right - left + 1;
if (length > this.data.Width)
{
throw new InvalidOperationException(
$"Error in KernelMap.CreateKernel({dataRowIndex},{left},{right}): left > this.data.Width");
}
this.ValidateSizesForCreateKernel(length, dataRowIndex, left, right);
Span<float> rowSpan = this.data.GetRowSpan(dataRowIndex);
@ -262,5 +258,15 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
float* rowPtr = (float*)Unsafe.AsPointer(ref rowReference);
return new ResizeKernel(left, rowPtr, length);
}
[Conditional("DEBUG")]
private void ValidateSizesForCreateKernel(int length, int dataRowIndex, int left, int right)
{
if (length > this.data.Width)
{
throw new InvalidOperationException(
$"Error in KernelMap.CreateKernel({dataRowIndex},{left},{right}): left > this.data.Width");
}
}
}
}

Loading…
Cancel
Save