Open david-maw opened 2 years ago
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.
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
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.
@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 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
Is there any workaround for the slow performance in ImageSharp for MAUI as of now 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.
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.
gsharedvt is only used on ios, not on android.
@SamMonoRT @steveisok any updates / progress in this area? It would be great to have ImageSharp running well on MAUI platforms and WASM!
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.
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>)
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.
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.
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?
This is a serious issue: ImageSharp cannot be used at all on MAUI Android.
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
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
The sample projects are gone!! 😔
I'd like to note. @beeradmoore has been helping me investigate Android performance issues with MAUI and we've measured that release mode with LLVM performs adequately with the following configuration.
<PropertyGroup Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'android' AND '$(Configuration)' == 'Release'">
<EnableLLVM>true</EnableLLVM>
<RunAOTCompilation>true</RunAOTCompilation>
<AndroidEnableProfiledAot>false</AndroidEnableProfiledAot>
</PropertyGroup>
However, we saw the significant performance issues highlighted in this issue running Android in Debug. I'm unsure whether removing the Release configuration condiftion here allows the library to build and run correctly. Guidance here would be appreciated.
Relevant discussion here:
@JimBobSquarePants the configuration above looks correct to me for Release
-mode for ImageSharp usage: "AOT everything" + LLVM.
In Debug-mode, we have the interpreter enabled by default, which probably doesn't play nice with hardware intrinsics / vector-based math. Does UseIntepreter=false
make things any better? This will, of course, prevent C# hot reload from working.
@JimBobSquarePants the configuration above looks correct to me for
Release
-mode for ImageSharp usage: "AOT everything" + LLVM.In Debug-mode, we have the interpreter enabled by default, which probably doesn't play nice with hardware intrinsics / vector-based math. Does
UseIntepreter=false
make things any better? This will, of course, prevent C# hot reload from working.
Thanks @jonathanpeppers I don't actually have the tooling to check but this sample application can be used to test against https://github.com/beeradmoore/ImageSharpMAUITest/tree/main
I added this which I changed from true to false to confirm it was being applied.
<PropertyGroup Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'android' AND '$(Configuration)' == 'Debug'">
<UseInterpreter>true</UseInterpreter>
</PropertyGroup>
Test run | JpgLoad | JpgResize | PngLoad | PngResize |
---|---|---|---|---|
UseInterpreter=true | 14289.9ms | 18556.9ms | 123.4ms | 344.7ms |
UseInterpreter=false | 1157.9ms | 1387.3ms | 35.4ms | 42.2ms |
What's interesting (and I guess explains it) is in my local source I still had this line enabled
Console.WriteLine("Vector.IsHardwareAccelerated: " + (System.Numerics.Vector.IsHardwareAccelerated ? "True" : "False"));
This was true for my last set of release mode tests. I just checked and both of these debug modes
Vector.IsHardwareAccelerated: False
With UseInterpreter=true
Vector.IsHardwareAccelerated
is false, but with UseInterpreter=false
Vector.IsHardwareAccelerated
is true.
EDIT: This comment was updated to fix the typo UseIntepreter
to UseInterpreter
and then tests were re-run.
Why would intrinsics be turned off for debug mode? Not-only is that a performance concern but it also vastly transforms the profile of the running code, limiting the ability to identify codegen issues.
@beeradmoore did I misspell above UseIntepreter
? Sorry. Should be UseInterpreter
?
Someone on the Mono team can comment, but it looks like hardware intrinsics for Vector is not implemented for the interpreter. It may be implemented for JIT on Android, if UseInterpreter=false
(spelled correctly) makes things faster.
Comment updated and tests re-run with correct spelling. Performance is a lot better, as expected Vector.IsHardwareAccelerated
is also reporting true.
Aside from hot reload, is there anything else to consider when using UseInterpreter=false
?
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
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