Skip to content
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

OutOfMemoryException when loading png image #248

Closed
aloisdeniel opened this issue Jun 15, 2017 · 41 comments
Closed

OutOfMemoryException when loading png image #248

aloisdeniel opened this issue Jun 15, 2017 · 41 comments

Comments

@aloisdeniel
Copy link

aloisdeniel commented Jun 15, 2017

Description

I get a OutOfMemoryException when I load a png image exported from the mac previewer.

icon

StackTrace:

  at System.Buffers.DefaultArrayPool`1[T].Rent (System.Int32 minimumLength) [0x000ab] in <7bf2c6844cae4e49ba93f68a56e51dad>:0 
  at ImageSharp.Formats.PngDecoderCore.ReadChunkData (ImageSharp.Formats.PngChunk chunk) [0x0000c] in <ba1825465e5b4bb4bfdd088e15832d97>:0 
  at ImageSharp.Formats.PngDecoderCore.ReadChunk () [0x00033] in <ba1825465e5b4bb4bfdd088e15832d97>:0 
  at ImageSharp.Formats.PngDecoderCore.Decode[TPixel] (System.IO.Stream stream) [0x00244] in <ba1825465e5b4bb4bfdd088e15832d97>:0 
  at ImageSharp.Formats.PngDecoder.Decode[TPixel] (ImageSharp.Configuration configuration, System.IO.Stream stream, ImageSharp.Formats.IPngDecoderOptions options) [0x00007] in <ba1825465e5b4bb4bfdd088e15832d97>:0 
  at ImageSharp.Formats.PngDecoder.Decode[TPixel] (ImageSharp.Configuration configuration, System.IO.Stream stream, ImageSharp.IDecoderOptions options) [0x00007] in <ba1825465e5b4bb4bfdd088e15832d97>:0 
  at ImageSharp.Image.Decode[TPixel] (System.IO.Stream stream, ImageSharp.IDecoderOptions options, ImageSharp.Configuration config) [0x00013] in <ba1825465e5b4bb4bfdd088e15832d97>:0 
  at ImageSharp.Image+<>c__DisplayClass36_0`1[TPixel].<Load>b__0 (System.IO.Stream s) [0x00000] in <ba1825465e5b4bb4bfdd088e15832d97>:0 
  at ImageSharp.Image.WithSeekableStream[T] (System.IO.Stream stream, System.Func`2[T,TResult] action) [0x0001b] in <ba1825465e5b4bb4bfdd088e15832d97>:0 
  at ImageSharp.Image.Load[TPixel] (ImageSharp.Configuration config, System.IO.Stream stream, ImageSharp.IDecoderOptions options) [0x00029] in <ba1825465e5b4bb4bfdd088e15832d97>:0 
  at ImageSharp.Image.Load[TPixel] (ImageSharp.Configuration config, System.String path, ImageSharp.IDecoderOptions options) [0x00019] in <ba1825465e5b4bb4bfdd088e15832d97>:0 
  at ImageSharp.Image.Load[TPixel] (System.String path) [0x00000] in <ba1825465e5b4bb4bfdd088e15832d97>:0 
  at ImageSharp.Image.Load (System.String path) [0x00000] in <ba1825465e5b4bb4bfdd088e15832d97>:0 
  at Mobile.Prerelease.Icon..ctor (System.String path) [0x0000f] in /Users/alois/Projects/Mobile.Manifests/Mobile.Manifests/Icons/Icon.cs:11 
  at Mobile.Prerelease.Tests.IconsTests.Annotate () [0x00002] in /Users/alois/Projects/Mobile.Manifests/Mobile.Manifests.Tests/IconsTests.cs:30 

Steps to Reproduce

using(var image = Image.Load(attachedFilePath))
{ ... }

System Configuration

  • ImageSharp version: 1.0.0-alpha9-00140
  • Environment (Operating system, version and so on): MacOS X 10.12.2
  • .NET Framework version: 4.6.1
  • Additional information: Visual Studio for Mac
@JimBobSquarePants
Copy link
Member

JimBobSquarePants commented Jun 15, 2017

Very odd, it's only a small png which I can decode/encode no problem.

Could you share some code samples? Are you running in 32bit or 64bit mode? Are there any other contributory factors that could be eating up memory?

@aloisdeniel
Copy link
Author

I made a very basic console project with minimal code and the exact same exception is thrown : ISFailure.zip.

I run it from Visual Studio for Mac :

Visual Studio Enterprise 2017 for Mac (Preview)
Version 7.1 Preview (7.1 build 583)
Installation UUID: 505008b4-7764-455f-aa41-f1aa53cc98d8
Runtime:
	Mono 5.2.0.104 (2017-04/4a0006f) (64-bit)
	GTK+ 2.24.23 (Raleigh theme)

	Package version: 502000104

Maybe it could be linked to the Preview version of VS ?

@tocsoft
Copy link
Member

tocsoft commented Jun 15, 2017

Looks like this might be a mono related issue, I've not managed to reproduce the exact error but I've definitely managed to reproduce related issues running the exe with the latest preview release of mono on windows (just failing to figure out how to debug it.) 😞

On windows i'm seeing:

Unhandled Exception:
ImageSharp.ImageFormatException: Unknown filter type.
  at ImageSharp.Formats.PngDecoderCore.DecodePixelData[TPixel] (System.IO.Stream compressedStream, ImageSharp.Image`1[TPixel] image) [0x000fd] in <ba1825465e5b4bb4bfdd088e15832d97>:0
  at ImageSharp.Formats.PngDecoderCore.ReadScanlines[TPixel] (System.IO.Stream dataStream, ImageSharp.Image`1[TPixel] image) [0x00017] in <ba1825465e5b4bb4bfdd088e15832d97>:0
  at ImageSharp.Formats.PngDecoderCore.Decode[TPixel] (System.IO.Stream stream) [0x00186] in <ba1825465e5b4bb4bfdd088e15832d97>:0
  at ImageSharp.Formats.PngDecoder.Decode[TPixel] (ImageSharp.Configuration configuration, System.IO.Stream stream, ImageSharp.Formats.IPngDecoderOptions options) [0x00007] in <ba1825465e5b4bb4bfdd088e15832d97>:0
  at ImageSharp.Formats.PngDecoder.Decode[TPixel] (ImageSharp.Configuration configuration, System.IO.Stream stream, ImageSharp.IDecoderOptions options) [0x00007] in <ba1825465e5b4bb4bfdd088e15832d97>:0
  at ImageSharp.Image.Decode[TPixel] (System.IO.Stream stream, ImageSharp.IDecoderOptions options, ImageSharp.Configuration config) [0x00013] in <ba1825465e5b4bb4bfdd088e15832d97>:0
  at ImageSharp.Image+<>c__DisplayClass36_0`1[TPixel].<Load>b__0 (System.IO.Stream s) [0x00000] in <ba1825465e5b4bb4bfdd088e15832d97>:0
  at ImageSharp.Image.WithSeekableStream[T] (System.IO.Stream stream, System.Func`2[T,TResult] action) [0x0001b] in <ba1825465e5b4bb4bfdd088e15832d97>:0
  at ImageSharp.Image.Load[TPixel] (ImageSharp.Configuration config, System.IO.Stream stream, ImageSharp.IDecoderOptions options) [0x00029] in <ba1825465e5b4bb4bfdd088e15832d97>:0
  at ImageSharp.Image.Load[TPixel] (ImageSharp.Configuration config, System.String path, ImageSharp.IDecoderOptions options) [0x00019] in <ba1825465e5b4bb4bfdd088e15832d97>:0
  at ImageSharp.Image.Load[TPixel] (System.String path) [0x00000] in <ba1825465e5b4bb4bfdd088e15832d97>:0
  at ImageSharp.Image.Load (System.String path) [0x00000] in <ba1825465e5b4bb4bfdd088e15832d97>:0
  at ISFailure.MainClass.Main (System.String[] args) [0x00001] in <6b69b8d368354f40acb76e45b659b70a>:0

My theory is something in Mono is corrupting the stream and causing the image load to fail.

@JimBobSquarePants
Copy link
Member

@tocsoft That would suggest an issue with DeflateStream on Mono to me.

@aloisdeniel Really not sure what to do about this. It seems to be a tooling issue. 😞

@tocsoft
Copy link
Member

tocsoft commented Jun 16, 2017

might be worth us investigating to see if we can port over our usage of DeflateStream over to https://github.com/icsharpcode/SharpZipLib and switch over to using that if

  1. we detect mono and
  2. we detect SharpZipLib is referenced

basically not include it in our outputted nuget dependencies just document the workaround for people using mono.

@JimBobSquarePants
Copy link
Member

Nah, I'd rather raise a bug in the Mono repo. SharpZibLib is way slower.

@carcer
Copy link

carcer commented Sep 27, 2017

We have ran into this issue on Windows. We had previously ran into this issue: #290, so we upgraded from 1.0.0-alpha9-00175 to 1.0.0-alpha9-00194. We also tried the OPs image, and that worked ok.
We're not able to share the image publicly as it is under NDA with a client, but can do so privately if you are able to supply an email address for us to send to.

Stacktrace

System.OutOfMemoryException: Exception of type 'System.OutOfMemoryException' was thrown.
   at System.Buffers.DefaultArrayPool`1.Rent(Int32 minimumLength)
   at ImageSharp.Formats.PngDecoderCore.ReadChunkData(PngChunk chunk)
   at ImageSharp.Formats.PngDecoderCore.ReadChunk()
   at ImageSharp.Formats.PngDecoderCore.Decode[TPixel](Stream stream)
   at ImageSharp.Formats.PngDecoder.Decode[TPixel](Configuration configuration, Stream stream)
   at ImageSharp.Image.Decode[TPixel](Stream stream, Configuration config)
   at ImageSharp.Image.<>c__DisplayClass43_0`1.<Load>b__0(Stream s)
   at ImageSharp.Image.WithSeekableStream[T](Stream stream, Func`2 action)
   at ImageSharp.Image.Load[TPixel](Configuration config, Stream stream, IImageFormat& format)
   at ImageSharp.Image.Load[TPixel](Stream stream)

System Configuration

ImageSharp version: 1.0.0-alpha9-00194
Environment (Operating system, version and so on): Window 10 N Pro 10.0.15063 Build 15063
.NET Framework version: 4.6.1
Additional information: Visual Studio 2017

@JimBobSquarePants
Copy link
Member

JimBobSquarePants commented Sep 27, 2017

@carcer What about the latest build? Drop me an email and I'll see if I can replicate it.

@carcer
Copy link

carcer commented Sep 27, 2017

We didn't notice there was a beta on nuget.org, apologies. Testing this now.

@carcer
Copy link

carcer commented Sep 27, 2017

@JimBobSquarePants Issue persists with beta0001

System.OutOfMemoryException: Exception of type 'System.OutOfMemoryException' was thrown.
   at System.Buffers.DefaultArrayPool`1.Rent(Int32 minimumLength)
   at SixLabors.ImageSharp.Formats.Png.PngDecoderCore.ReadChunkData(PngChunk chunk)
   at SixLabors.ImageSharp.Formats.Png.PngDecoderCore.ReadChunk()
   at SixLabors.ImageSharp.Formats.Png.PngDecoderCore.Decode[TPixel](Stream stream)
   at SixLabors.ImageSharp.Formats.Png.PngDecoder.Decode[TPixel](Configuration configuration, Stream stream)
   at SixLabors.ImageSharp.Image.Decode[TPixel](Stream stream, Configuration config)
   at SixLabors.ImageSharp.Image.<>c__DisplayClass43_0`1.<Load>b__0(Stream s)
   at SixLabors.ImageSharp.Image.WithSeekableStream[T](Stream stream, Func`2 action)
   at SixLabors.ImageSharp.Image.Load[TPixel](Configuration config, Stream stream, IImageFormat& format)
   at SixLabors.ImageSharp.Image.Load[TPixel](Stream stream)

I have a note of your email and will send the image over now, if you want to edit it out of your reply

@JimBobSquarePants
Copy link
Member

Great. It's very late here now so I'll have a look tomorrow.

@JimBobSquarePants
Copy link
Member

@carcer I've tested against the image you sent (thanks for that) using the latest code and everything is decoding correctly for me.

Silly question but.... You're disposing of your images yeah?

@carcer
Copy link

carcer commented Sep 29, 2017

@JimBobSquarePants Fair question, I double checked and every thing looks. We've been using the alpha for a while now and haven't had an issues, saving the image as a JPG and processing it works fine. Still, it does point to something we might be doing. I will look into it some more over the weekend. 👍

@JimBobSquarePants
Copy link
Member

Good man, let me know how you get on. 😄

@houseme-brandon
Copy link

I am getting this issue as well
Running 1.0.0-beta0002 with dotnet core 2.0 hosted on Azure

We are calling the image operations concurrently. Does this affect the memory allocation?

Is there some work around we can use?
Is it related to Png only?

            using (var image = Image.Load(Base64StreamToByteArray(base64ImageStream)))
            {
                var calculatedWidth = maxHeight * image.Width / image.Height;
                image.Mutate(x => x.Resize(calculatedWidth, maxHeight));
                image.Mutate(x => x.AutoOrient());
                return image.ToBase64String(ImageFormats.Png);
            }
System.OutOfMemoryException:
   at System.Buffers.ConfigurableArrayPool`1+Bucket.Rent (System.Private.CoreLib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e)
   at System.Buffers.ConfigurableArrayPool`1.Rent (System.Private.CoreLib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e)
   at SixLabors.ImageSharp.Memory.Buffer`1..ctor (SixLabors.ImageSharp, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null)
   at SixLabors.ImageSharp.ImageFrame`1..ctor (SixLabors.ImageSharp, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null)
   at SixLabors.ImageSharp.ImageFrameCollection`1..ctor (SixLabors.ImageSharp, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null)
   at SixLabors.ImageSharp.Image`1..ctor (SixLabors.ImageSharp, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null)
   at SixLabors.ImageSharp.Formats.Jpeg.GolangPort.OrigJpegDecoderCore.PostProcessIntoImage (SixLabors.ImageSharp, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null)
   at SixLabors.ImageSharp.Formats.Jpeg.JpegDecoder.Decode (SixLabors.ImageSharp, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null)
   at SixLabors.ImageSharp.Image.Decode (SixLabors.ImageSharp, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null)
   at SixLabors.ImageSharp.Image+<>c__DisplayClass43_0`1.<Load>b__0 (SixLabors.ImageSharp, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null)
   at SixLabors.ImageSharp.Image.WithSeekableStream (SixLabors.ImageSharp, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null)
   at SixLabors.ImageSharp.Image.Load (SixLabors.ImageSharp, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null)
   at SixLabors.ImageSharp.Image.Load (SixLabors.ImageSharp, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null)

@JimBobSquarePants
Copy link
Member

@houseme-brandon

Looking at the stacktrace that error is throwing in the jpeg decoder, it hasn't hit png yet.

What's your Base64StreamToByteArray method doing? are you pooling arrays or creating a new one each time?

@houseme-brandon
Copy link

houseme-brandon commented Nov 22, 2017

Thanks @JimBobSquarePants here is the method you asked about.

        private static byte[] Base64StreamToByteArray(Stream base64Stream)
        {
            var base64String = Encoding.ASCII.GetString(StreamToByteArray(base64Stream));
            var bytes = Convert.FromBase64String(base64String);
            return bytes;
        }
        private static byte[] StreamToByteArray(Stream input)
        {
            using (MemoryStream ms = new MemoryStream())
            {
                input.CopyTo(ms);
                var array = ms.ToArray();
                return array;
            }
        }

I have not configured anything regarding arrays so I assume it is doing the default. Not sure what that would be?
Sorry I am not clued up on the concept of pooling arrays vs creating new ones. Happy to do my research if you could point me in the right direction.

I would think that we are creating new arrays every time because each image task is being created from the MVC controller

@antonfirsov
Copy link
Member

antonfirsov commented Nov 22, 2017

@houseme-brandon looks like your doing a chain of unnecessary transformations on very large data buffers. Can you try CryptoStream to decode your base64 input, and feed the result stream to Image.Load() to avoid those allocations?

@JimBobSquarePants
Copy link
Member

@houseme-brandon I can immediately see two non-pooled arrays both in Convert.FromBase64String and ToArray

That will be where you are using up all your memory.

Look up ArrayPool<T> in System.Buffers you should be able to use that to at least copy the input stream to. Removing the allocation from decoding the encoded string though I don't know.

@antonfirsov
Copy link
Member

[ The example I found is not the best one, because it's writing the stream, you need to read it :) ]

@houseme-brandon
Copy link

houseme-brandon commented Nov 22, 2017

I have implemented the CyrptoStream which removes the non-pooled arrays as far as I can see.

I am handling the disposing of objects as far as I can see.
I can not see an efficient use of ArrayPool after the CryptoStream change

public static string ResizeImage(Stream base64ImageStream, int maxHeight)
        {
            using (var result = new CryptoStream(base64ImageStream, new FromBase64Transform(), CryptoStreamMode.Read))
            using (var image = Image.Load(result))
            {
                base64ImageStream.Dispose();
                var calculatedWidth = maxHeight * image.Width / image.Height;
                image.Mutate(x => x.Resize(calculatedWidth, maxHeight));
                image.Mutate(x => x.AutoOrient());
                return image.ToBase64String(ImageFormats.Png);
            }
        }

Still getting the out of memory exception. The only option I can see is to use an ArrayPool directly as I retrieve the image. Am I still missing something?

@antonfirsov
Copy link
Member

@houseme-brandon have you any information about the typical sizes of the images you test your application with? Can you share some of them?

Also: can you try to attach a memory profiler to your application following these instructions, and share your results with us? (Make sure you choose "Memory usage" instead of CPU). It would be very helpful both for you and us! :)

@houseme-brandon
Copy link

@antonfirsov typical sizes is difficult to say. But I have an example of a batch of images that I got permission to share.
Most of the images are 4032 x 3024 and about 4 MB
There are a few that are much larger.

This is a link to the pictures I was talking about with an image of azure's memory profile.
https://mega.nz/#F!5wJVgCzJ!u7JVdAFKxnA9QAFTxEK9gw

The memory profile does seem to peak at about 40% and that is when I am loading images. It is strange to me that it never gets higher than that but complains about out of memory but I assume that this is something to do with the garbage collector.

I have not seen this issue when running the program locally. It only occurs on azure. I will try run the azure in debug mode with a remote debugger to see if I can get more information about the memory profile.

I really appreciate all the help because our users are getting an unpleasant experience at the moment!

@antonfirsov
Copy link
Member

antonfirsov commented Nov 22, 2017

@houseme-brandon those are pretty large images! After a quick calculation, it seems that each 4032 x 3024 sized Image<Rgba32> would consume about 50MB after decompression! There is nothing to do about this, this is the cost of uncompressed in-memory representation of an RGBA image.

What is the memory limit of your VM/app service? Also: Make sure you are running it in a 64 bit process.

My current assumption is that, our internal array pool is growing beyond your memory limit before getting into a balanced state when it stops growing.

As a first step, it would be nice to prove this. Unfortunately, the memory profile you posted doesn't help. We need something much more detailed, with information about the object graphs. VS memory profiler, or ReSharper memory profiler could do the job.

@houseme-brandon
Copy link

The server has 7GB ram. I did set it to run as a 64 bit process but the fact that it is running out of memory at 40% makes me think it is actually running as a 32 bit process. I just checked and it is starting the as x86 even though I specified x64 in azure and built it as AnyCpu. Makes sense that these issues are happening in azure and not on my local PC.

I am struggling to get it to start it as 64 bit on azure but will get back to you when I get it right.

Going to try and get some more detailed memory profile as well.

@JimBobSquarePants
Copy link
Member

@houseme-brandon If you're running in 32bit mode it will be a lack of contiguous memory that's your issue. Shift to 64bit and you should be fine.

I'm actually impressed that we were able to run images of those dimensions. I've seen System.Drawing balk at less in 32bit mode.

@houseme-brandon
Copy link

Thanks! So the issue started by changing to dotnet core. We were System.Drawing successfully on dotnet framework because it was running as a 64 bit process. It lost the runtime environment when we changed to dotnet core. Now that it is finally able to start as a 64 bit process again it seems to run flawlessly!

Sorry for hikackjing this issue since it turned out to not be related . . . or even an issue

@JimBobSquarePants
Copy link
Member

Discussions like this are great for other readers who run into the same problem. I'm just glad you got it sorted. 😄

@alexsorokoletov
Copy link

Wondering if anyone solved that for Azure AppService running .NET Core 2 (not 2.1). Looks like having 64bit Core runtime requires some effort and not just a toggle in AppService properties
https://blogs.msdn.microsoft.com/webdev/2018/01/09/64-bit-asp-net-core-on-azure-app-service/

@JimBobSquarePants
Copy link
Member

We've got #431 in the works just now which should dramatically reduce the amount of memory we consume when pooling plus give some options to configure your own memory providers.

That said, I'd always recommend 64bit for anything that requires working with images in the megapixels.

@alexsorokoletov
Copy link

I see. Thing is it's crashing now :) so I can't wait for 431 though I would appreciate it by updating NuGet next release 👏

My image is small in size (2.8M) but large in pixels - 7500x5000. Also looking at the code and comparing how people do resize images using this library, I want to try another trick - if that helps, I'll be good.

Since we're here, the issue itself was annoying not because of the crash but because of the fact no developer could reproduce the problem - Visual Studio trained everyone to have lots of RAM.

Would be nice to have some kind of diagnostics that would pop the issue in development and not in production when it's a bit late :

@JimBobSquarePants
Copy link
Member

Are you using the beta2 release? @antonfirsov did some interim work in a recent nightly to reduce pooling consumption that might help you. I would grab that from MyGet and see if it helps.

Also, if you can get away without transparency try loading the image in Rgb24.

That's a big image though which will always cause problems on 32bit systems due to the limits on available contiguous memory.

@alexsorokoletov
Copy link

Beta 2. Is there anything fresher/better?
Yes, I saw that improvement, thank you @antonfirsov 👍

I don't need transparency probably, though I would love to have it there just cause it could be...
Is there a way I can write that code in adaptive way and if there is not enough mem, try using 24 bits instead of 32?

Part of the problem is that I can limit binary file size for upload but then image could be small as a file but could have large size in pixels and it will lead to OOM anyway.

@antonfirsov
Copy link
Member

@alexsorokoletov are you using our nightlies? If not, can you try them? There was some improvement with #436.

If yous still have the crashes with the nightlies, can you help us by describing your use case? How many requests do you have before the crash? Are those large images outliers or typical?

@antonfirsov
Copy link
Member

@alexsorokoletov
Copy link

alexsorokoletov commented Feb 9, 2018

Meanwhile, I've refactored code and instead of using Clone + Resize to generate multiple sizes of the image I start with the largest needed size and use Mutate + Resize going down to smaller.

Deployed and so far runs good in Azure 32bit on this image (for now).

I can try nightly builds, yes.

On your questions, Anton:

describing your use case

Users upload pictures for avatars to the website, we keep large and small thumbnail of these pictures.
PNG/JPEG.

How many requests do you have before the crash

Before changing Clone + Resize -> Mutate - just one :)
Right now trying to crash Mutate-based version.

Are those large images outliers or typical?

Typical, testing on images from https://unsplash.com/

@alexsorokoletov
Copy link

I did some really simple tests on nightly 1.0.0-dev000706 and beta 1.0.0-beta0002 with Rgba32 and Rgb24:

Nightly:

Job=Nightly1.0.0-dev000706 LaunchCount=1 RunStrategy=ColdStart
TargetCount=10 WarmupCount=3

Method Mean Error StdDev Gen 0 Gen 1 Gen 2 Allocated
Rgba32 2.738 s 0.2428 s 0.1606 s 1000.0000 1000.0000 1000.0000 399.96 MB
Rgb24 3.153 s 0.1689 s 0.1117 s 1000.0000 1000.0000 1000.0000 359.96 MB

Job=Beta1.0.0-beta0002 LaunchCount=1 RunStrategy=ColdStart
TargetCount=10 WarmupCount=3

Method Mean Error StdDev Allocated
Rgba32 2.353 s 0.1432 s 0.0947 s 8.21 MB
Rgb24 2.798 s 0.0935 s 0.0618 s 8.18 MB

I wanted to compare memory savings from beta/nightly and also Rgba32 vs Rgb24 but for some reason allocation is wrong for beta build (maybe it was built way too long ago).

For some reason beta works faster ~14%, and Rgba32 doesn't give huge memory savings so I will stay with beta0002 and Rgba32.

Thank you :)

@JimBobSquarePants
Copy link
Member

For some reason beta works faster ~14%, and Rgba32 doesn't give huge memory savings so I will stay with beta0002 and Rgba32.

You can't stay on beta forever... 😄

If you read #431 You'll understand why there is more memory allocated for your benchmark.

We were creating too large an object pool in the beta so while you are seeing only low allocations in your benchmark, you're not seeing the pre-allocated memory that was set aside for the array pool. Memory that is locked away for nothing else but ImageSharp to use for the entire run-time of the application. The nightly reduces this pooling and allows the garbage collector to much more effectively free up contiguous memory and avoid Out of Memory Exceptions. Smaller images that lie within the pool bounds still use pooled memory.

The beta is also faster than the nightly because we were incorrectly missing alpha pre-multiplication as part of the resampling process which let to errors when resizing images with semi transparent pixels. Adding the overhead of premultiplication slows performance down but also fixes what would have been quite a major bug.

Rgba32 is faster because certain paths have been heavily optimized for that pixel format. We'll add optimizations for Rgb24 and others in the future.

@JimBobSquarePants
Copy link
Member

Can someone test this issue please against the latest nightlies. We've squashed several png bugs and completely rewritten memory management to half the memory used by default.

@alexsorokoletov
Copy link

@JimBobSquarePants once we plan to update the website where we use the package, I'll be glad to test the new version.
In many scenarios, we just used images from UnSplash, they come in different sizes and content.

Maybe we can setup a dotnet benchmark-based test that tracks allocations and tries to get like 10 images from unsplash and convert them?

@JimBobSquarePants
Copy link
Member

Thanks @alexsorokoletov

I'm fairly certain we've squashed the issues around this now but yeah, it could be an idea to do something like that. 👍

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

7 participants