-
-
Notifications
You must be signed in to change notification settings - Fork 888
Faster Transforms #1118
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Faster Transforms #1118
Conversation
src/ImageSharp/Processing/Processors/Transforms/Resize/ResamplerExtensions.cs
Outdated
Show resolved
Hide resolved
Codecov Report
@@ Coverage Diff @@
## master #1118 +/- ##
==========================================
+ Coverage 82.21% 82.22% +0.01%
==========================================
Files 678 678
Lines 29118 29117 -1
Branches 3300 3300
==========================================
+ Hits 23938 23942 +4
+ Misses 4484 4480 -4
+ Partials 696 695 -1
Continue to review full report at Codecov.
|
antonfirsov
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Did not have the chance for an extensive review yet, sharing a few comments and API-related concerns for now.
| public int StartIndex { get; } | ||
| public int StartIndex | ||
| { | ||
| [MethodImpl(InliningOptions.ShortMethod)] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think the JIT is smart enough to do this without our intervention. I see the BCL code pushing back a lot on applications for explicit AggressiveInlining these days, I suppose the JIT does it more reliably with really short methods now.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's more about expressing intent than anything. The JIT can ignore our suggestion but I believe we should still declare that we want the method inlined.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm that makes sense, but in trivial cases like property getters it gets overly verbose and hurts readability IMO. (Technically all autoproperies should be marked then.)
src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeKernelMap.cs
Outdated
Show resolved
Hide resolved
src/ImageSharp/Processing/Processors/Transforms/Resize/ResamplerExtensions.cs
Outdated
Show resolved
Hide resolved
|
I think I have an idea, will prototype it in a few days (and also do a more comprehensive review here). |
|
@antonfirsov I just had another crack at this. I've introduced a new interface /// <summary>
/// Implements an algorithm to alter the pixels of an image via a resampling transforms.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
public interface IResamplingImageProcessor<TPixel> : IImageProcessor<TPixel>
where TPixel : struct, IPixel<TPixel>
{
/// <summary>
/// Applies a resampling transform with the given sampler.
/// </summary>
/// <typeparam name="TResampler">The type of sampler.</typeparam>
/// <param name="sampler">The sampler to use.</param>
void ApplyTransform<TResampler>(in TResampler sampler)
where TResampler : struct, IResampler;
}
/// <summary>
/// Encapsulates an interpolation algorithm for resampling images.
/// </summary>
public interface IResampler
{
/// <summary>
/// Gets the radius in which to sample pixels.
/// </summary>
float Radius { get; }
/// <summary>
/// Gets the result of the interpolation algorithm.
/// </summary>
/// <param name="x">The value to process.</param>
/// <returns>
/// The <see cref="float"/>
/// </returns>
float GetValue(float x);
/// <summary>
/// Applies a transformation upon an image.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="processor">The transforming image processor.</param>
void ApplyTransform<TPixel>(IResamplingImageProcessor<TPixel> processor)
where TPixel : struct, IPixel<TPixel>;
}With implementations like this. /// <summary>
/// The function implements the nearest neighbor algorithm. This uses an unscaled filter
/// which will select the closest pixel to the new pixels position.
/// </summary>
public readonly struct NearestNeighborResampler : IResampler
{
/// <inheritdoc/>
public float Radius => 1;
/// <inheritdoc/>
[MethodImpl(InliningOptions.ShortMethod)]
public float GetValue(float x) => x;
/// <inheritdoc/>
[MethodImpl(InliningOptions.ShortMethod)]
public void ApplyTransform<TPixel>(IResamplingImageProcessor<TPixel> processor)
where TPixel : struct, IPixel<TPixel>
=> processor.ApplyTransform(in this);
}Everything else is now internal. I moved things about a little to reorganize also. Performance remains the same as the benchmarks. |
Btw, if I am not mistaken, the method |
|
I’m not so concerned by that method being inlined (it would be nice). Much more important is that the |
|
@JimBobSquarePants the |
src/ImageSharp/Processing/Processors/Transforms/Automorphic/AffineTransformProcessor{TPixel}.cs
Outdated
Show resolved
Hide resolved
src/ImageSharp/Processing/Processors/Transforms/Automorphic/AutomorphicTransformUtilities.cs
Outdated
Show resolved
Hide resolved
src/ImageSharp/Processing/Processors/Transforms/IResamplingImageProcessor{TPixel}.cs
Outdated
Show resolved
Hide resolved
|
Imma gonna merge this. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM. Looks like I spotted everything in during my previous lazy reviews.

Prerequisites
Description
IResamplerimplementations into readonly structBug Fixes
The attached image shows the difference in output following the updates. RGB color information no longer leaks beyond the bounds of the transformed result.
Benchmarks
Massive performance improvements in Affine and Projective transforms.
Rotate
Before
After
Skew
Before
After
Resize
Resize improvements are less dramatic which is expected. NET Core 3.1 shows a nice improvement though bringing it inline with NETFX. A slight improvement overall.
Before
After