I have an application that essentially shows a slide show of images from a file. The loading of the images slows down over time. Putting in some timing code, I've identified the slowing line of code as the ConvertFromString line below:
var imgSrcConverter = new ImageSourceConverter();
imgSrc = (ImageSource) imgSrcConverter.ConvertFromString(imgFilePath);
Over the course if 15 hours, the execution of this line went from taking ~70ms to >400ms.
Can anyone offer any suggestions as to why this would happen and what can be done to prevent and/or improve the situation?
Could you load the images into memory on startup, and then just cycle through them?
Related
My question is very easy. What is the correct point for loading data from service/database in the page or view?
I am using OnAppearing method:
protected override void OnAppearing()
{
base.OnAppearing();
_ = Task.Run(async () =>
{
await Task.Delay(500);
await LoadData();
});
}
private async Task LoadData()
{
this.Items = await this.LoadDataFromDatabase();
}
Definition of items:
[ObservableProperty]
private ObservableCollection<Product>? _items = null;
The database returns a maximum of 20 items.
I use ScrollView with HorizontalWrapLayout and DataTemplate for items.
DataTemplate:
Border
-- Grid with rows
--- Label
--- Label
--- Label
My problem is that the application is not smooth. Flyout menu is not smooth. It seems like UI wait for loading data. Loading data from database takes +/- 100ms however displaying data (20 items) on the page takes another 3-4sec. User experience is not very smooth in Release mode. The same result is also in Mobile/Tablet/Virtual devices/Phyical devices.
I also measured the speed of HorizontalWrapLayout:
Measure: ~10ms
ArrangeChildren: ~15ms
I have many of these logos in android:
Skipped 363 frames! The application may be doing too much work on its main thread.
What can be wrong? Is it possible that this is MAUI problem? I load the data and the whole display happens automatically via bindable property = Items. What is the correct point of loading data (Constructor in model or OnAppearing or.... )?
I uses Net7.0 and last version of MAUI => 7.0.52
Visual Studio 2022 version 17.5 Preview 2
Android API 33, Android v13 (virtual devices also phyzical devices)
PC: Intel(R) Core(TM) i9-10850K CPU # 3.60GHz, 32 GB ram
I am providing more information because my assumption is that the problem is not in weak devices.
Thank you for any advice, recommendations, suggestions, etc.
Edit 1:
Now I noticed a very interesting thing. HorizontalWrapLayoutManager is very fast (I listed the times above). However, Measure and ArrangeChildren are called 128-time to display data. (~10ms + ~15ms) * 128 = 3200ms. A very interesting thing is that the widthConstraint parameter for Manager is always the same, but the heightConstraint changes alternately between 1493 and Infinity. Is there any logic why the manager is called 128 times when displaying 8 items?
Edit 2:
I reduced my xaml step by step. After removing each parent view, the recalculation of the Manager is called less times. By removing almost everything, recalculation in the manager is called only 4 times. It also seems that if there is a parent Grid element with Auto size for Column or Row, the number of recalculations increases. I understand the logic of the recalculation, but it causes the display of data to be very slow in the final. Accordint to my logs, methods (Measure and Arrange) on the View inside the Manager cause a final delay when called repeatedly. Would not the solution be to save the data and call these methods only once?
First, I want to point out that I see this:
await Task.Delay(500);
In a question related to performance and speed.
I do not know what you plan to do with this, and why you are doing it. But whatever the reason, please remove that line.
Second, if you are using Android, please make sure you test your performance in release. It may differ in order of magnitudes compared to the performance you see in debug.
If this does not help, try CollectionView, or DataGrid, and see if it will get better.
Edit: Just want to add, you are doing it correctly. I often use OnAppearing for loading and displaying data. (as command in my ViewModels)
My summarization:
I use HorizontalWrapLayout with custom implementation for HorizontalWrapLayoutManager.
I foud out that Manarger is very fast, however ArrangeChildren and Measure are called more times. The number depends on the number of parents and the layout of the screen. The number of calls also affects the auto size in parent's Grids. Repeated recalculation and calls to Measure/ArrangeChildren in HorizontalWrapLayoutManager cause a large delay when displaying data in HorizontalWrapLayout.
I solved the problem by updating the HorizontalWrapLayoutManager. Manager stores the data returned from Measure and Arrange for each child. The first call will be recalculated. In the event that the items, padding, spacing, widthConstraint or other essential values have not changed during subsequent calls, the Manager no longer calls Measure and Arrange for each view, but returns data from the cache.
Small test:
HorizontalWrapLayoutManager without cache = 128 calls => ~3100ms
HorizontalWrapLayoutManager with cache = 128 calls => ~100ms
So far, I have not found any problems that would result from the cache. In the future, however, it is not excluded that recalculation will have to occur when other properties of HorizontalWrapLayout are changed.
This question refers to Codename One only.
I have a Form with a Tabs and each tab can contain an arbitrary number of images (taken from the Gallery).
Result: after few images (eight images from the gallery), on my Android device I get:
java.lang.OutOfMemoryError: Failed to allocate a 5683356 byte allocation with 1845080 free bytes and 1801KB until OOM
at dalvik.system.VMRuntime.newNonMovableArray(Native Method)
at android.graphics.BitmapFactory.nativeDecodeStream(Native Method)
at android.graphics.BitmapFactory.decodeStreamInternal(BitmapFactory.java:737)
at android.graphics.BitmapFactory.decodeStream(BitmapFactory.java:703)
at com.codename1.impl.android.c.b(AndroidImplementation.java:2037)
at com.codename1.r.z.a(Image.java:531)
at cool.teammate.apps.frontend.b.e.a$1.a(BasePageForm.java:208)
at com.codename1.r.l.b.a(EventDispatcher.java:349)
at com.codename1.impl.android.c.a(AndroidImplementation.java:7336)
at com.codename1.impl.android.CodenameOneActivity$6.run(CodenameOneActivity.java:527)
at com.codename1.r.r.n(Display.java:1298)
at com.codename1.r.r.l(Display.java:1242)
at com.codename1.r.r.k(Display.java:1130)
at com.codename1.r.aq.run(RunnableWrapper.java:120)
at com.codename1.impl.b$1.run(CodenameOneThread.java:60)
at java.lang.Thread.run(Thread.java:776)
Also iOS crashes, but I haven't the log.
Each image is scaled before showing (using the class FixedSizeButton, that I reported in this question). Any suggestion?
I guess that the dispose() method of Image can be useful, but it's written to "DO NOT CALL THIS METHOD UNLESS YOU KNOW WHAT YOU ARE DOING". Is it so problematic? For example, can it help if I dispose an Image after saving it to file and/or after scaling?
I also guess that System.gc() can be useful.
However, if few photos are so problematic... how can I make a photo gallery inside a Form without this kind of issues? Same problem to load hundreds of photo post inside an InfiniteContainer...
Note that the memory issue happens regardless if I insert the images as icon of a Button or as filled background of the Button.
Thank you for your suggestion.
You don't need to call dispose() or gc() for a proper application. You're trying to load a 5mb image which I'm assuming is a 5mb JPEG. That's probably from a 9+ mega pixel camera which means the decompressed size would be roughly 9 x 4 == 36MB!
So 36 x 8 = 288mb of RAM.
this.image = Image.createImage(this.imageWidth, this.imageHeight, 0xFFdddddd);
What's imageWidth/Height?
You might be taking a lot of RAM here...
this.setIcon(this.image);
EasyThread scalingThread = EasyThread.start("FixedSizeButton-ScalingImg-" + fileName);
Since you're creating multiple threads you might have all 8 images in RAM at once.
A better way is to use one image scaling thread. An even better approach is avoiding scaled altogether and using ImageIO to scale the image without loading it into RAM.
I have the requirement to update an image in the App over and over again (displaying some generated data).
But after several intervals the app crashes with out of memory.
I'm updating the image with this line:
(that.detailImage.nativeElement).src = DummyImage.image1;
setting the image as base64-String. I already tried using res:// and external references and even local files. The problem stays: it keeps the memory.
Reducing the size of the image helps only to delay the crash, I even got the crash with an 1kb image, but it took some minutes.
You can find the "running" code at:
https://bitbucket.org/WarEagle/implementationproblems/src/944eedf10a14d60ec421e274760b3c2c3d9ae9a1/ImageTest/?at=master
I'm really interested in understanding what I'm doing wrong. Every solution I found so far said: "make the image smaller" but this only delays the problem.
Greetings
Torsten
p.s. don't wonder about the = undefined in the example-code, this is done because I'm setting the same image over and over again, than it works, but if I update the src-property with another image, it crashes, for this example I used = undefined to reproduce the problem without uploading thousands of images for changing them around.
I created my own deformer, and I assigned to huge resolution mesh.
At that time, if open a new scene or a existing scene file, it takes a long time to open it.
Comparing to maya's native deformer, it is significant to wasting time.
It seems that I have to do something on my deformer to clean up, but I don't know the fuction is runned when a new scene opened.
Depending on how you implemented your node, it will be deform() and possibly compute(). When the scene loads up Maya needs to prime the scene graph so all of the deformers will execute. You can verify this by adding a debug print inside those methods but that is the likely culprit. You may find the DG Profiler useful for spotting the culprit
I am working on a project where we were asked to "patch" (they don't want a lot of time spent on development as they soon will replace the system) a system implemented under ExtJS 4.1.0.
That system is used under a very slow and non-stable network connection. So sometimes the stores don't get the expected data.
First two things that come to my mind as patches are:
1. Every time a store is loaded for the first time, wait 5 seconds and try again. Most times, a page refresh fix the problem of stores not loading.
Somehow, check detect that no data was received after loading a store and, try to get it again.
This patches should be executed only once to avoid infinite loops or unnecessary recursivity, given that it's ok that some times, it's ok that stores don't get any data back.
I don't like this kind of solutions but it was requested by the client.
This link should help with your question.
One of the posters suggests adding the below in an overrides.js file which is loaded in between the ExtJs source code and your applications code.
Ext.util.Observable.observe(Ext.data.Connection);
Ext.data.Connection.on('requestexception', function(dataconn, response, options){
if (response.responseText != null) {
window.document.body.innerHTML = response.responseText;
}
});
Using this example, on any error instead of echoing the error in the example you could log the error details for debugging later and try to load again. I would suggest adding some additional logic into this so that it will only retry a certain number of times otherwise it could run indefinitely while the browser window is open and more than likely crash the browser and put additional load on your server.
Obviously the root cause of the issue is not the code itself, rather your slow connection. I'd try to address this issue rather than any other.