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

Horrible performance when loading local files #376

Closed
MizzleDK opened this issue Jan 30, 2014 · 31 comments
Closed

Horrible performance when loading local files #376

MizzleDK opened this issue Jan 30, 2014 · 31 comments

Comments

@MizzleDK
Copy link

Hi guys,

First of all, fantastic job with this library. I really like the API.

I've been experiencing some rather annoying performance issues when loading local images on the device using Picasso. In short, it's causing major lag when scrolling and I'm really not sure why it's happening.

I'm almost certain the problem isn't with my layout or code. It's working wonderfully without the Picasso call in my Adapter's getView(). It's worth mentioning that the library is NOT causing lag when loading images from the web - only with local content.

Here's a screenshot from my application when loading images while scrolling (on a Nexus 10):
untitled-2

As you can see, it's not pretty. This is using the standard Picasso.with(context).load("fileUrl").into(ImageView) stuff. Like I said, if I load network images from the web, it's perfectly fine. The issue only occurs with local files.

Is this a known issue? It occurs on all my devices - some worse than others. My Nexus 5 isn't affected as much, but both my Nexus 7 (2013) and Nexus 10 are suffering horribly from it. They're capable of displaying more images at once, so that might explain it.

I've encountered the issues using the latest release version (2.1.1) and on a snapshot from the current master branch.

@MizzleDK
Copy link
Author

Just thought I'd add another screenshot from the application without the Picasso call in getView(). Again, Nexus 10:

untitled-3

@dnkoutso
Copy link
Collaborator

Do you have a sample app I can use to work with on this? Interesting you mention that web urls work fine.

How big are the files and how big are the bitmaps you load?

@MizzleDK
Copy link
Author

I sadly don't have a sample app at the moment. I guess I can create one, if it's super necessary :-)

The images are 500 x 750 pixels on my Nexus 10. They change depending on the device's screen resolution, but they're the same resolution on the Nexus 5, which isn't as prone to the issue.

The file size is anywhere between 50 - 500 KB, depending on the details in the image. The app in question is Mizuu, if you want to check out how the images are loaded. There's no free version, but I'll be happy to refund it, so you can test using it. Here's a Play store link: https://play.google.com/store/apps/details?id=com.miz.mizuu

@JakeWharton
Copy link
Collaborator

We'd really like to see one or two sample images that exhibit the behavior and the minimum amount of code to demonstrate. It doesn't have to be a full sample, just the images and the exact code you're using to load them.

@dnkoutso
Copy link
Collaborator

Are the bitmaps 500 x 750 or the ImageView dimensions? Can be a big difference.

@MizzleDK
Copy link
Author

@JakeWharton Regarding sample images, it's all the "w500" images from TMDb, i.e. http://image.tmdb.org/t/p/w500/dYhyHXRkxoDoTUVddmOp9q5ECNL.jpg or http://image.tmdb.org/t/p/w500/5gRn74vfX0dmvgCrxK4ztpQ4j7s.jpg.

The application scans user-defined file sources, identifies the files found and download movie information, including cover art for the movies. On high resolution devices, it downloads the "w500" images and leaves them untouched, so those images would qualify as sample images.

@dnkoutso That's the bitmap dimensions. The ImageViews are 422 x 633 pixels on my Nexus 10 in landscape mode. Also worth noting that the bitmap dimensions are identical for web images and local images.

@dnkoutso
Copy link
Collaborator

Can you run a traceview and paste some logcat output when you scroll?

@MizzleDK
Copy link
Author

Logcat is basically lots of GC_FOR_ALLOC:

untitled-2

The GC is running when loading web images as well, but it doesn't happen as often, mainly due to the device not being able to download the images as fast as I'm scrolling, I'd imagine.

I'll post a traceview shortly.

@MizzleDK
Copy link
Author

Here's a trace:

http://mizuu.tv/mizuu.trace

@dnkoutso
Copy link
Collaborator

Can you also paste a screenshot of the app please?

@MizzleDK
Copy link
Author

Here's one from the Nexus 10:

screenshot_2014-01-19-21-24-30

@MizzleDK
Copy link
Author

And here's one with overdraw debugging enabled:

untitled-3

@dnkoutso
Copy link
Collaborator

Out of curiosity if you try another image lib (if its easy) does it exhibit the same problem?

@MizzleDK
Copy link
Author

I used Universal Image Loader before. It happened occasionally, but wasn't as bad. Also, it was possible to pause image loading during scroll and flings with that library, so that was obviously a way to avoid the occasional lag. Is there any particular image library you'd like me to try out?

@dnkoutso
Copy link
Collaborator

No thats fine. You could do the same with Picasso by invoking it only when scrolling has finished instead of in your getView() method.

I think what happens is too many bitmaps load too fast causing this to occur. Can you attempt changing your Picasso instance with a single threaded executor?

@MizzleDK
Copy link
Author

I've actually already tried doing that :-) It helped a bit. Still not great, but it was an improvement.

@dnkoutso
Copy link
Collaborator

I'll need to get a Nexus 10 and attempt this here...in the meantime let me know if you find anything thats causing it.

Recommend using allocation tracker to find out why there are so many allocations.

@dnkoutso
Copy link
Collaborator

Which one did you try? invoking with single thread or after scroll?

@MizzleDK
Copy link
Author

Sorry, with a single thread.

Not sure how I'd load images after scrolling ended. AFAIK It won't work if I use an OnScrollListener with some sort of boolean flag.

@dnkoutso
Copy link
Collaborator

I'll have to take a look on a sample app on Nexus 10. In the meantime let me know if you come up with some other clue around this.

@dnkoutso
Copy link
Collaborator

Use a higher memory cache on a Nexus 10 perhaps?

@MizzleDK
Copy link
Author

It's set to 20% of the available heap. Also using the largeHeap manifest option, as the application is rather heavy on images.

I just tried turning off the largeHeap option, but that didn't change anything.

@MizzleDK
Copy link
Author

You mentioned that "I think what happens is too many bitmaps load too fast causing this to occur."

I think that might be the reason why it's lagging as well. Would it be possible to somehow introduce a few hundred milliseconds delay for each request?

@MizzleDK
Copy link
Author

This is what it looks like if I set it to use just one thread:

untitled-2

The downside of this is of course that it seems non-responsive to the user, if one of the images fail to load or take a long time loading. While it doesn't really happen with local content, it happens quite a lot with network content.

@dnkoutso
Copy link
Collaborator

wonder if you can split into two picasso instances...but use the same cache for them. Haven't gotten into a nexus 10 yet

@MizzleDK
Copy link
Author

I've decided to do the following for now. It seems to be working quite all right, and I get performance similar to the screenshot in my previous post:

private static ThreadPoolExecutor getThreadPoolExecutor() {
        if (mThreadPoolExecutor == null)
            mThreadPoolExecutor = new ThreadPoolExecutor(1, 2, 2, TimeUnit.SECONDS,
                new LinkedBlockingQueue<Runnable>(), new PicassoThreadFactory());
        return mThreadPoolExecutor;
    }

    static class PicassoThreadFactory implements ThreadFactory {
        public Thread newThread(Runnable r) {
            return new PicassoThread(r);
        }
    }

    private static class PicassoThread extends Thread {
        public PicassoThread(Runnable r) {
            super(r);
        }

        @Override public void run() {
            android.os.Process.setThreadPriority(android.os.Process.THREAD_PRIORITY_BACKGROUND);
            super.run();
        }
    }

It's still not optimal as I'm sure you'd agree, but it's better than what I started with.

@MizzleDK
Copy link
Author

MizzleDK commented Feb 2, 2014

Just thought I'd pop in again and tell you how I've been able to further improve the butter in my app 👍

With Picasso 2.2 I'm changing the Bitmap.Config to RGB_565 as the difference between it and ARGB_8888 is negligible in grid overviews. I'm only able to spot a difference when I zoom in 400% in Photoshop and even then it's really nothing. Using RGB_565 will obviously mean that the application uses less memory and thereby causes fewer GC's.

I've also noted that Picasso's resize() call helps to lower the memory usage even further, so if I use that with the exact dimensions of the ImageView in question, it'll help quite a bit and reduce the number of GC's as well.

Correct me if I'm wrong, but it seems that the resize() call actually does resize to the specified dimensions. I never thought that it was guaranteed.

@dnkoutso
Copy link
Collaborator

dnkoutso commented Feb 2, 2014

Awesome! Great to hear!

Yes resize(w, h) will resize to the exact dimensions. You just cut every single bitmap in half by using RGB_565 since its pixel is stored with 2 bytes for this config and you decode even less by using exact size!

Really good to hear.

@dnkoutso
Copy link
Collaborator

dnkoutso commented Feb 2, 2014

I'll be closing this for now.

@dnkoutso dnkoutso closed this as completed Feb 2, 2014
@mheras
Copy link

mheras commented Nov 13, 2014

Hi @dnkoutso @JakeWharton

I know this was closed since @MizzleDK made the scrolling better by using RGB_565, but I cannot do that in my app.
I also know that I can use the new tag system in 2.4 and pause the requests when scrolling, but unfortunately I cannot do that in my app either.
I'm wondering if there's any other way to limit the use of the heap, as whenever the lib loads an image, more memory is being allocated in the heap, which causes the app to lag. I tried using Universal Image Loader, but the same thing happens.

Thanks.

@dnkoutso
Copy link
Collaborator

You could limit the executor used to be single threaded. This would avoid allocating too much simultaneously but would slow down perceived loading as one image would load at a time.

Alternatively you can try applying this: #174 which allows to re-use bitmaps from a pool to avoid gc.

With Android L GC doesn't appear to be an issue so much so I am not sure we will be applying a pool anytime soon.

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

No branches or pull requests

4 participants