Imagesharp: Image resize performance when compared to other libraries

Created on 24 Oct 2018  路  10Comments  路  Source: SixLabors/ImageSharp

Several months ago we where evaluating image processing libraries for their resize performance. We compared several of them on two platforms: .NET Core 2.1 (Windows) and .NET CLR 4.7 (also Windows). I can present comparison between CoreCompat.System.Drawing.v2 (5.2.0-preview1-r131) and ImageSharp (1.0.0-beta0005):

BenchmarkDotNet=v0.11.1, OS=Windows 10.0.17134.345 (1803/April2018Update/Redstone4)
Intel Core i7-7660U CPU 2.50GHz (Max: 1.70GHz) (Kaby Lake), 1 CPU, 4 logical and 2 physical cores
.NET Core SDK=2.1.403
  [Host]     : .NET Core 2.1.5 (CoreCLR 4.6.26919.02, CoreFX 4.6.26919.02), 64bit RyuJIT
  DefaultJob : .NET Core 2.1.5 (CoreCLR 4.6.26919.02, CoreFX 4.6.26919.02), 64bit RyuJIT


| Method | Mean | Error | StdDev | Scaled | ScaledSD | Allocated |
|------------------------------------ |---------:|----------:|---------:|-------:|---------:|-----------:|
| System_Drawing_resize_jpg_2520x1575 | 328.6 ms | 7.306 ms | 10.00 ms | 1.00 | 0.00 | 681.54 KB |
| ImageSharp_resize_jpg_2520x1575 | 670.4 ms | 21.978 ms | 64.80 ms | 2.04 | 0.21 | 99074.4 KB |

As I remember ImageSharp was advertised as a new high performance library, yet we approached to it several times in the last half year, and it never stood a chance against System.Drawing or SkiaSharp. Are doing something wrong here?

Here's the code we used for the benchmark:

[Benchmark(Baseline = true)]
        public Stream System_Drawing_resize_jpg_2520x1575()
        {
            using (var image = System.Drawing.Image.FromFile(ImagePath))
            {
                var width = 2000;
                var height = 2000 * image.Height / image.Width;

                using (var bmp = new System.Drawing.Bitmap(width, height))
                {
                    bmp.SetResolution(image.HorizontalResolution, image.VerticalResolution);
                    using (var g = System.Drawing.Graphics.FromImage(bmp))
                    {
                        g.CompositingMode = System.Drawing.Drawing2D.CompositingMode.SourceCopy;
                        g.CompositingQuality = System.Drawing.Drawing2D.CompositingQuality.HighQuality;
                        g.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic;
                        g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality;
                        g.PixelOffsetMode = System.Drawing.Drawing2D.PixelOffsetMode.HighQuality;

                        using (var wrap = new System.Drawing.Imaging.ImageAttributes())
                        {
                            wrap.SetWrapMode(System.Drawing.Drawing2D.WrapMode.TileFlipXY);
                            var rect = new System.Drawing.Rectangle(0,0, width, height);
                            g.DrawImage(image, rect, 0, 0, image.Width, image.Height, System.Drawing.GraphicsUnit.Pixel, wrap);
                            var stream = new MemoryStream();
                            bmp.Save(stream, System.Drawing.Imaging.ImageFormat.Jpeg);
                            return stream;
                        }
                    }
                }   
            }
        }

        [Benchmark]
        public Stream ImageSharp_resize_jpg_2520x1575()
        {
            using (var input = File.OpenRead(ImagePath))
            {
                var image = SixLabors.ImageSharp.Image.Load(input);
                var width = 2000;
                var height = 2000 * image.Height / image.Width;

                var output = new MemoryStream();
                image.Clone(i =>
                    i.Resize(width, height, KnownResamplers.Bicubic))
                    .SaveAsJpeg(output);
                return output;
            }
        }
performance

Most helpful comment

@Horusiath I don't think we ever advertised ImageSharp as a "high performance library", I would rather say it's a continously evolving library.

I really hope, that for the final 1.0 release we will be able to close the performance gap compared to System.Drawing, which is a theoretical limit with the current RyuJIT capabilities. The integration of dotnet/corefx#22940 into .NET Core 3.0 / .NET Framework 4.8 will open up new opportunities in the future.

Please note that just about 2 years ago we started from being 10-20x slower, and we achieved these improvements working in our free time. We are investigating the possibilities to find a sustainable business model to fund the project, so we can invest more time into the performance improvements.

All 10 comments

What happens when you use Mutate instead of Clone?

And dispose of your image...
And read #733

@Horusiath I don't think we ever advertised ImageSharp as a "high performance library", I would rather say it's a continously evolving library.

I really hope, that for the final 1.0 release we will be able to close the performance gap compared to System.Drawing, which is a theoretical limit with the current RyuJIT capabilities. The integration of dotnet/corefx#22940 into .NET Core 3.0 / .NET Framework 4.8 will open up new opportunities in the future.

Please note that just about 2 years ago we started from being 10-20x slower, and we achieved these improvements working in our free time. We are investigating the possibilities to find a sustainable business model to fund the project, so we can invest more time into the performance improvements.

And of course the remarks on Mutate and Dispose are valid!

@dlemstra I'm sharing the results after using mutate and disposing the image:

BenchmarkDotNet=v0.11.1, OS=Windows 10.0.17134.345 (1803/April2018Update/Redstone4)
Intel Core i7-7660U CPU 2.50GHz (Max: 1.70GHz) (Kaby Lake), 1 CPU, 4 logical and 2 physical cores
.NET Core SDK=2.1.403
  [Host]     : .NET Core 2.1.5 (CoreCLR 4.6.26919.02, CoreFX 4.6.26919.02), 64bit RyuJIT
  DefaultJob : .NET Core 2.1.5 (CoreCLR 4.6.26919.02, CoreFX 4.6.26919.02), 64bit RyuJIT

| Method | Mean | Error | StdDev | Median | Scaled | ScaledSD | Allocated |
|------------------------------------ |---------:|----------:|---------:|---------:|-------:|---------:|------------:|
| System_Drawing_resize_jpg_2520x1575 | 329.3 ms | 9.164 ms | 11.59 ms | 327.9 ms | 1.00 | 0.00 | 681.54 KB |
| ImageSharp_resize_jpg_2520x1575 | 600.8 ms | 21.504 ms | 63.41 ms | 633.5 ms | 1.83 | 0.20 | 49922.45 KB |

@antonfirsov Don't worry - I'm heavily contributing to some OSS projects for over 4 years. I know the struggle of that model ;) I hope you guys will find a good path for yourself to provide means for continuous improvement.

Btw. aren't you using SIMD (at least as part of System.Numerics.Vectors) already?

@Horusiath Could you try the latest nightlies please? I'd like to see how the recent improvements fair.

We were using SIMD already in it's limited capacity and @antonfirsov has already added some further improvements utilizing the new Core 2.1 API's both to the resampling algorithm and scanline processing.

System.Numerics.Vectors is a limited subset. We miss operations as basic as shuffle or shifting(<<, >>) .

@Horusiath FYI my benchmark results for a very similar scenario, downscaling and resaving a 2048 x 1536 Jpeg to 512 x 384.

These results are a bit better than yours, here is a speculative explanation:

We probably perform better on high-end CPU-s operating on high frequencies, because this setup is accelerating the effect of Intel's state-of-art AVX2 implementations. It is very likely that the GDI+ Drawing API-s do not utilize the newest SIMD instructions, because by benchmarking only Resize, it looks like we are already faster than System.Drawing.

On the other hand, the GDI+ jpeg codecs beat us because they are powered by WIC as backend.

On the other hand, the GDI+ jpeg codecs beat us because they are powered by WIC as backend.

I'd give my right arm to see the source code. It's about as fast as a jpeg decoder gets.

I'm closing this. We'll open jpeg performance specific issues when they come around.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

artem-avanesov picture artem-avanesov  路  3Comments

Inumedia picture Inumedia  路  3Comments

olivif picture olivif  路  3Comments

marcpabst picture marcpabst  路  3Comments

xakep139 picture xakep139  路  4Comments