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

Very Poor ImageSharp Performance in MAUI Android #71210

Open
david-maw opened this issue Jun 16, 2022 · 69 comments
Open

Very Poor ImageSharp Performance in MAUI Android #71210

david-maw opened this issue Jun 16, 2022 · 69 comments

Comments

@david-maw
Copy link

Description

A piece of code which executes in under 100 ms in Xamarin forms on Android or Windows and under 50 ms in Windows MAUI takes over 30 seconds in Android MAUI.

Steps to Reproduce

  1. Clone repository https://github.com/david-maw/ResizeImageMaui.git from github
  2. Build the project
  3. Run it on Windows
  4. Click the button, on Windows the image will be converted to greyscale in under a second (50 ms in my case).
  5. Try it again on Android, this time the conversion will take much longer (in my case it was over 30 seconds but less than a minute.

FYI there's a similar Xamarin Forms project at https://github.com/david-maw/ResizeImageXamarin.git, in my testing this took about 90 ms to load on Window (so MAUI was almost twice as fast), and just under a second on the Android emulator (so MAUI was 30+ times slower).

This uses SixLabors.ImageSharp to do the image processing so it is likely the implementation of something in that library that's wildly slow on .NET 6 on Android.

Version with bug

6.0 Release Candidate 3

Last version that worked well

Unknown/Other

Affected platforms

Android, I was not able test on other platforms

Affected platform versions

Android 11

Did you find any workaround?

No

Relevant log output

No response

@jfversluis
Copy link
Member

@jonathanpeppers this seems like something for you :)

@janseris
Copy link

Did you try Release configuration for the benchmarking?

@jonathanpeppers
Copy link
Member

jonathanpeppers commented Jun 16, 2022

@david-maw yes you should try a Release build, and maybe try "full" AOT:

<PropertyGroup Condition=" '$(Configuration)' == 'Release' ">
  <RunAOTCompilation>true</RunAOTCompilation>
  <AndroidEnableProfiledAot>false</AndroidEnableProfiledAot>
</PropertyGroup>

Release builds use profiled AOT by default, and SixLabors.ImageSharp would not be in our built-in profile.

@NonameMissingNo
Copy link

@jonathanpeppers
Mi 11i (Android):
Debug: 35.841 Seconds
Release: 7.212 Seconds (2nd run: 7.137 Seconds)
Release with full AOT: 7.151 Seconds

For reference Windows (Ryzen 5 5500U):
Debug: 210 ms
Release: 165 ms

@NonameMissingNo
Copy link

NonameMissingNo commented Jun 16, 2022

Release is a lot better, but I don't think it should be 43 times slower than the windows Release build

@jonathanpeppers
Copy link
Member

@NonameMissingNo did you already get a .speedscope file, to see where the problem is?

https://github.com/xamarin/xamarin-android/blob/main/Documentation/guides/tracing.md

I'll do this myself when I get a chance to look at this, but that might point out specifically where the problem is.

@david-maw
Copy link
Author

@janseris no, I wasn't actually trying to benchmark, I was trying to debug some code which calls SixLabors.ImageSharp, I did give it a try with the additions suggested by @jonathanpeppers though, alas release builds do not seem to be working for me. This one fails with

1>C:\Program Files\dotnet\packs\Microsoft.Android.Sdk.Windows\32.0.415\targets\Microsoft.Android.Sdk.Aot.targets(78,5): error : Mono Ahead of Time compiler - compiling assembly C:\Users\david\Documents\Develop\Samples\ResizeImageMaui\ResizeImageMaui\obj\Release\net6.0-android\android-arm\aot-in\Mono.Android.dll
1>C:\Program Files\dotnet\packs\Microsoft.Android.Sdk.Windows\32.0.415\targets\Microsoft.Android.Sdk.Aot.targets(78,5): error : AOTID AE96AD80-0388-6F60-8F1D-297A5B9027D9
1>C:\Program Files\dotnet\packs\Microsoft.Android.Sdk.Windows\32.0.415\targets\Microsoft.Android.Sdk.Aot.targets(78,5): error : Compiled: 155736/155736
1>C:\Program Files\dotnet\packs\Microsoft.Android.Sdk.Windows\32.0.415\targets\Microsoft.Android.Sdk.Aot.targets(78,5): error : Executing the native assembler: "C:\Program Files\dotnet\packs\Microsoft.Android.Sdk.Windows\32.0.415\tools\binutils\arm-linux-androideabi-as"   -mfpu=vfp3 -o obj\Release\net6.0-android\android-arm\aot\armeabi-v7a\Mono.Android\temp.s.o obj\Release\net6.0-android\android-arm\aot\armeabi-v7a\Mono.Android\temp.s
1>Done building project "ResizeImageMaui.csproj" -- FAILED.

Not much of a problem for me actually, I'm mostly interested in debug builds, I don't move to a release build until the pre-release performance checks.

Just as well @NonameMissingNo tested it, thanks for that.

@jonathanpeppers
Copy link
Member

@david-maw if a general Release build fails, you might file an issue here with a diagnostic MSBuild log:

https://github.com/xamarin/xamarin-android/issues

Thanks!

@NonameMissingNo
Copy link

@jonathanpeppers It would seem like that the ImageSharp is the one doing the work
image
maui-app_20220616_184909.speedscope.zip

@jonathanpeppers
Copy link
Member

Thanks!

So I wonder if the code here:

https://github.com/SixLabors/ImageSharp/blob/7bd0e03792d2b5141fcfb046cb92329e9c0df582/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeWorker.cs#L168-L199

I bet there are some runtime optimizations for Span/Vector4 that we have on Windows + CoreClr, and we don't have those in Mono.

@david-maw
Copy link
Author

@jonathanpeppers

Thanks for the hint on the diagnostic MSBuild Log. I'll look into the Release build issue a bit more and file a bug if it does not seem to be a local problem. The issue was building the MAUI Android version, not Xamarin.Forms Android. The error was in the AOT compiler which is presumably why the Debug build was ok.

Back to the original topic,

The Xamarin.Forms Android Release Build completed the second and subsequent image conversions in under 500 ms and a Debug build took 1.5 s, both a far cry from the MAUI Android numbers (just over 33 s for Debug, I can't test Release).

If the Android performance differential is a mono limitation why is the performance of the same app in Xamarin so much better? Is it that it is a different Mono version?

@jonathanpeppers
Copy link
Member

Debug builds also default to UseInterpreter=true (this enables hot reload). This isn't an option at all in Xamarin.

So you could turn it off in your project, but then hot reload wouldn't work either.

@david-maw
Copy link
Author

Thanks, that would probably work for the occasional test but so far at least it looks easier said than done, since for now isn't recognized (I note there are a number of Issues relating to handling it for multi-targeted project files.

@jonathanpeppers jonathanpeppers changed the title Very Poor Performance in MAUI Android Very Poor ImageSharp Performance in MAUI Android Jun 20, 2022
@jonathanpeppers
Copy link
Member

I had a conversation with some of the Mono folks -- going to move this to dotnet/runtime.

@dotnet-issue-labeler
Copy link

I couldn't figure out the best area label to add to this issue. If you have write-permissions please help me learn by adding exactly one area label.

@jonathanpeppers jonathanpeppers transferred this issue from dotnet/maui Jun 23, 2022
@ghost ghost added the untriaged New issue has not been triaged by the area owner label Jun 23, 2022
@jonathanpeppers jonathanpeppers added area-CoreLib-mono and removed untriaged New issue has not been triaged by the area owner labels Jun 23, 2022
@jonathanpeppers
Copy link
Member

/cc @steveisok @lambdageek

@lambdageek
Copy link
Member

  1. We should try running this on the net7 runtime packs. There's been arm64 intrinsics work in net7 in mono.
  2. The code that @jonathanpeppers pointed out (https://github.com/SixLabors/ImageSharp/blob/7bd0e03792d2b5141fcfb046cb92329e9c0df582/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeWorker.cs#L168-L199) looks like it could be tested in a console app. that should make it easier to look at what the JIT is doing.

@jjxtra
Copy link

jjxtra commented Dec 12, 2022

Switched to skiasharp and performance is 100x better

@JimBobSquarePants
Copy link

JimBobSquarePants commented Dec 12, 2022

That’s expected since the actual operating code is not powered by the runtime and doesn’t suffer from this issue. On a desktop with a working JIT ImageSharp will comfortably beat SkiaSharp’s performance for both resizing and matrix color transformation

@3egirlsdream
Copy link

when can fix this bug?

@jjxtra
Copy link

jjxtra commented Apr 24, 2023

Use skiasharp

@JimBobSquarePants
Copy link

That’s not a very productive answer is it?

Managed code should work well cross platform. If there are issues then that is due to limitations of the AOT compiler.

@hez2010
Copy link
Contributor

hez2010 commented Apr 24, 2023

is due to limitations of the AOT compiler

The issue is mono-specific, not AOT-specific. I tried NativeAOT and didn't find any performance issues with ImageSharp. The performance of given repro in the top post is parity between RyuJIT and NativeAOT (both under 50ms).

@mjsb212
Copy link

mjsb212 commented Apr 25, 2023

Just ran into this same issue when I ported my Xamarin project to MAUI. Tested with .net 6 & 7 (MAUI) running on ANDROID. ImageSharp loading any files is SUPER SLOW - On XAMARIN everything still loads fine as expected. The original Xamarin code ported to MAUI:

FileStream fs = new FileStream(file.FullPath, FileMode.Open, FileAccess.Read);
Image image = SixLabors.ImageSharp.Image.Load(fs);

...Takes about 20 - 30 SECONDS to load any image into stream

@lewing
Copy link
Member

lewing commented Apr 25, 2023

This issue won't be fixed in net6 or 7 any changes are too large to to backport. There have been changes in net8 that will likely impact the performance positively.

@mjsb212
Copy link

mjsb212 commented Apr 25, 2023

So in .net 8 this will be fixed? I switched to SkiSharp as others said & performance is 100x better...but I love imagesharp for many things so this is a bummer

@EvgenyMuryshkin
Copy link

Same issue.
Native MAUI Downsize works, but rotates image.
SkiaSharp works, but also rotates images.

ImageSharp is the only one that works and does not rotate, but slow :(
Hope for MAUI 8, migration journey has been terrible.

@hallatore
Copy link

@EvgenyMuryshkin Are you trying to load an image with rotation metadata?

Try load it with image instead of bitmap. That ensures correct rotation.

using var image = SKImage.FromEncodedData(imageStream);
return SKBitmap.FromImage(image);

@EvgenyMuryshkin
Copy link

@EvgenyMuryshkin Are you trying to load an image with rotation metadata?

Try load it with image instead of bitmap. That ensures correct rotation.

using var image = SKImage.FromEncodedData(imageStream);
return SKBitmap.FromImage(image);

Does not work on iPad, Android seems ok

@e012345678
Copy link

Is there any workaround for the slow performance in ImageSharp for MAUI as of now 2024?

@JimBobSquarePants
Copy link

JimBobSquarePants commented Jan 4, 2024

You’re asking the wrong people. Ask the Maui team.

EDIT. Note to self. Never use GitHub on your mobile. I thought the previous comment was in the ImageSharp downstream issue.

@MichalStrehovsky
Copy link
Member

You’re asking the wrong people. Ask the Maui team.

Mono team, is there a way to rule out this is not (or confirm it is) caused by gsharedvt? Are there any events generated when gsharedvt code is hot? ImageSharp is generic heavy, it also uses generic virtual methods. Notice the hot stacks have methods instantiated over structs like Rgba32. I could easily see the perf being atrocious if every operation on a pixel changes from "load 32 bits into a register" to "memcpy a statically unknown number of bytes from X to Y". AFAIK Mono AOT lacks the analysis to figure out all necessary generic virtual method bodies and we often end up running gsharedvt versions.

@vargaz
Copy link
Contributor

vargaz commented Jan 5, 2024

gsharedvt is only used on ios, not on android.

@Redth
Copy link
Member

Redth commented Jan 6, 2024

@SamMonoRT @steveisok any updates / progress in this area? It would be great to have ImageSharp running well on MAUI platforms and WASM!

@vargaz
Copy link
Contributor

vargaz commented Jan 6, 2024

Does this really affect wasm ? I tried:

        for (int i = 0; i < 10; ++i) {
            var fs = typeof (Test).Assembly.GetManifestResourceStream ("Wasm.Console.V8.Sample.foo.jpg");
            var image = SixLabors.ImageSharp.Image.Load(fs);
        }

And it runs in about 2s on wasm in AOT mode on a 2000x1500 image, so each decode takes about 0.2s.

@vargaz
Copy link
Contributor

vargaz commented Jan 6, 2024

The wasm AOT performance can probably be improved by pre seeding more generics, some methods are still interpreted because the AOT compiler doesn't know about them, like:
void SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.JpegImagePostProcessor:ConvertColorsInto<SixLabors.ImageSharp.PixelFormats.Rgba32> (SixLabors.ImageSharp.ImageFrame`1<SixLabors.ImageSharp.PixelFormats.Rgba32>)

@JimBobSquarePants
Copy link

How did you determine which method required pre seeding? We’ve added a lot of method seeding to the library already but it’s been without guidance.

What are the plans going forward to remove the requirement for pre seeding? It feels like a horrible hacky workaround. I would expect compiler analysis to do much better.

@vargaz
Copy link
Contributor

vargaz commented Jan 21, 2024

The AOT compiler does appear to have problems figuring out which instances to generate. In this specific case, the caller is
ImageDecoderUtilities:Decode<Rgba32> which calls IImageDecoderInternals::Decode on an argument. So in theory, the aot compiler could figure out that the call could possible go to JpegDecoderCore::Decode<Rgba32> and generate that instance. Currently, this kind of analysis is not done.

The ImageSharp codebases unfortunately makes heavy use of generics, interfaces, valuetype generic arguments, etc., which is not very friendly to static compilation.

@vargaz vargaz removed their assignment Mar 22, 2024
@JimBobSquarePants
Copy link

I can see that this has been removed from a performance backlog and nobody is now assigned. What can be done to change this state?

@sharpwood
Copy link

This is a serious issue: ImageSharp cannot be used at all on MAUI Android.

@MichalStrehovsky
Copy link
Member

I can see that this has been removed from a performance backlog and nobody is now assigned. What can be done to change this state?

Cc @vitek-karas

@JimBobSquarePants
Copy link

Since there's been no movement here, I've created a potential workaround for this particular issue in the ImageSharp codebase, but I have no means to test my changes. Would anyone be able to compile and test the code in this PR

SixLabors/ImageSharp#2762

@JimBobSquarePants
Copy link

The sample projects are gone!! 😔

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