-
-
Notifications
You must be signed in to change notification settings - Fork 416
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
Slowdown in MagickImage.ToByteArray on arm64 Mac #1447
Comments
Have you tested this with the most recent version? |
Yes, same issue. On 4 Oct 2023, at 06:49, Dirk Lemstra ***@***.***> wrote:
Have you tested this with the most recent version?
—Reply to this email directly, view it on GitHub, or unsubscribe.You are receiving this because you authored the thread.Message ID: ***@***.***>
|
Do you get the "old" performance when you do this instead: using var stream = new MemoryStream();
image.Write(stream);
var bytes = stream.ToArray(); |
Writing the image into a MemoryStream and then getting the array takes < 1ms on both new and old version. [CORRECTION: ~20-25ms] |
I really wonder what kind of measurements you are using. This is what was happening inside the old version of |
csproj snippet:
Run test with 13.3.0
Run test with 13.1.0
The output of I'll pull this into a project if this isn't enough, let me know. I'll send you the image. |
My mistake, not 1ms, 23ish. I guess I must have read 21 as '1' :( |
... just so I can follow on at home: So versions that perform differently are post a change where ToByteArray was changed to look a lot like the Stream writer:
... what you are alluding to is the memory stream is how ToByteArray used to look:
and Write looks a lot like what ToByteArray was changed to:
I have to agree, these are very very similar things. |
I ran both versions through a profiler. In my test code using version 13.3.0 it's spending a lot of time in Seek because Seek is spending its time calling System.Array.Resize 7269 times. I can't find any calls to Resize in the profile for 13.1.0. ByyeArrayWrapper.Seek in 13.3.0 looks like:
... could the interaction of this with the calling function be the smoking gun. In 13.1.0 StreamWrapper is used and there Seek delegates to the Stream passed in, which presumably is doing something quite different in Seek than the ByteArrayWrapper as it’s a core .NET library and likely doesn’t increment in the way ByteArrayWrapper does. |
... this also explains why the slow down is related to the complexity of the image (something I noticed yesterday). If the image is entirely white (one of my test cases that didn't exhibit as bad a slowdown) then perhaps it's uncompressed in a very small number of chunks as the information content is low, so the array size is grown to size in a small number of Resize calls. I don't know the codebase very well at all, am I heading in the right direction here? If I am right then I should be able to replicate this in a test in the Native repo. I'll give that a go in the next 3-4 weeks. If I replicate it then I'll think about a fix if you would like. |
Thanks for all the research. I was suspecting this was the issue but I was not sure about it. The Would it be possible to share an image that I can use to reproduce the issue? I might be able to find a workaround for this myself then. Maybe this would work |
I thought I had shared an image but didn’t so I will do that ASAP. On 5 Oct 2023, at 20:31, Dirk Lemstra ***@***.***> wrote:
Thanks for all the research. I was suspecting this was the issue but I was not sure about it. Would it be possible to share an image that I can use to reproduce the issue? I might be able to find a workaround for this myself then. Maybe this would work ResizeBytes(newOffset + BufferSize);?
—Reply to this email directly, view it on GitHub, or unsubscribe.You are receiving this because you authored the thread.Message ID: ***@***.***>
|
I would imagine so. It'd likely reduce the number of Resize calls which would also have the handy feature of reducing the chances of heap fragmentation requiring the whole array is moved in memory as part of resizing so it can maintain a contiguous chunk of memory (assuming that's how C# arrays work, I've only been coding in C# for less than a month part time so I know very little. I would think you don't need to allocate memory on a Seek as no Read or Write is happening. You could consider Seek() a declaration of intent. If you took that approach then you only need the array resized when something happens, e.g. when reads or writes overlap a region that was intended to be resized via a previous seek and has yet to be resized. Write already handles something similar to this case but it'd need adjusting and you'd need to add Resize support to Read to handle when Read is reading off the end of the allocated area into the "to be allocated" area. It also means you can Seek around as you wish without incurring cost until you do something. That being said, I don't know what the call pattern of the Readers/Writers is in this library. Re: reproduction, a simple test that just seeks forward 1 byte at a time is going to produce this problem on arm64 (I guess). I'd implement this test myself but with my inexperience I can't get the stuff building on osx-arm64. |
I've just checked the C# Memory Stream implementation and it takes the approach I describe above, which is why that performs in a more consistent fashion. Look at Seek() in https://github.com/microsoft/referencesource/blob/master/mscorlib/system/io/memorystream.cs. |
Could you please share a test file I could use for this? I would like to investigate this but having no test file is blocking me. |
...sent to the email address on your github profile |
Thanks for the file and I can see what is happening. I really think I should simplify the implementation of the |
I made some changes to the implementation and this should fix the performance again in the next release. |
That’s great, it’ll let me upgrade :) thank you. On 16 Oct 2023, at 19:41, Dirk Lemstra ***@***.***> wrote:
I made some changes to the implementation and this should fix the performance again in the next release.
—Reply to this email directly, view it on GitHub, or unsubscribe.You are receiving this because you authored the thread.Message ID: ***@***.***>
|
Magick.NET version
Magick.NET-Q8-arm64 13.1.2
Environment (Operating system, version and so on)
arm64, Mac M1, .NET 7.0.11/7.0.401
Description
takes minutes in 13.1.2 when it used to take seconds for a 3k/4k file of reasonable image complexity. All the additional time is in ToByteArray. In 13.1.2 there's an overflow exception and in 13.1.0 the speed is as I expect.
I scaled the image down so I can attach it (if I can work out how to do that) and the perforce is 25ms for 13.1.0 and 4123ms for 13.1.2. Note: the slow down is less extreme when there is less detail in the image.
Steps to Reproduce
Create a PSD (RGB/8 is fine), make it say 1000 x 1000 and ensure it has some detail in it. Time the code above. Increase the image size and complexity to make the timing difference larger. My Mac has 64GB of RAM and an Apple M1 Max processor.
The text was updated successfully, but these errors were encountered: