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

GIF animations are very slow. Possible solution and workaround. #3575

Closed
yuriy-budiyev opened this issue Mar 21, 2019 · 21 comments
Closed

GIF animations are very slow. Possible solution and workaround. #3575

yuriy-budiyev opened this issue Mar 21, 2019 · 21 comments

Comments

@yuriy-budiyev
Copy link
Contributor

yuriy-budiyev commented Mar 21, 2019

Glide Version: 4.9.0

Integration libraries: none

Device/Android Version: Pixel 3, Android Q beta

Issue details / Repro steps / Use case background: Unable to set animation executor in GlideBuilder, setted value is ignored in build method (line 487 in v4.9.0). animationExecutor field is unused.
This causes GIF animations to run on default one or two threaded executor wrich is VERY SLOW, so if you have many GIF's, there are 1fps slideshow or even slower.
Also, by profiling, we can see that one animation thread is used by Glide and fully loaded (never idle), so device's cpu can't be utilized properly.

Glide is used with AppGlideModule, and builder methods called there.

image

image

@yuriy-budiyev
Copy link
Contributor Author

yuriy-budiyev commented Mar 21, 2019

BTW, this is fixed in master branch:

@yuriy-budiyev
Copy link
Contributor Author

yuriy-budiyev commented Mar 21, 2019

A workaround is to update verstion to 4.10.0-SNAPSHOT and:

builder.setAnimationExecutor(
    GlideExecutor.newSourceExecutor(
        DEFAULT_THREAD_POOL_SIZE,
        "animation",
        GlideExecutor.UncaughtThrowableStrategy.DEFAULT
        )
    )

We can't use newAnimationExecutor() because it will stay on one thread until it's queue is full, which is impossible while using PriorityBlockingQueue with it's Integer.MAX_VALUE ramainingCapacity().

From PriorityBlockingQueue source code:

/**
* Always returns {@code Integer.MAX_VALUE} because
* a {@code PriorityBlockingQueue} is not capacity constrained.
* @return {@code Integer.MAX_VALUE} always
*/
public int remainingCapacity() {
    return Integer.MAX_VALUE;
}

@yuriy-budiyev
Copy link
Contributor Author

yuriy-budiyev commented Mar 21, 2019

So, this problem should be fixed anyway. In other case GIF animations will play very slow.

@yuriy-budiyev
Copy link
Contributor Author

yuriy-budiyev commented Mar 21, 2019

Profiling with newSourceExecutor() and 4 animation threads (4.10.0-SNAPSHOT, Pixel 3, Android Q beta):

image

Animations are smooth.

@yuriy-budiyev yuriy-budiyev changed the title Unable to set animation executor in GlideBuilder, GIF animations are very slow GIF animations are very slow Mar 21, 2019
@yuriy-budiyev yuriy-budiyev changed the title GIF animations are very slow GIF animations are very slow. Possible solution and workaraound. Mar 21, 2019
@yuriy-budiyev yuriy-budiyev reopened this Mar 22, 2019
@stale
Copy link

stale bot commented Mar 29, 2019

This issue has been automatically marked as stale because it has not had activity in the last seven days. It will be closed if no further activity occurs within the next seven days. Thank you for your contributions.

@stale stale bot added the stale label Mar 29, 2019
@stale stale bot closed this as completed Apr 5, 2019
@sjudd sjudd reopened this Apr 5, 2019
@sjudd sjudd added bug and removed enhancement labels Apr 5, 2019
@sjudd
Copy link
Collaborator

sjudd commented Apr 5, 2019

Thanks for investigating this, I appreciate the detail!

@rizwan321
Copy link

@yuriy-budiyev can you please explain what do you mean by updating to 4.10.0-SNAPSHOT ??
as i am also facing slow animation in gif

@yuriy-budiyev
Copy link
Contributor Author

@rizwan321 In version 4.9.0 you can't set custom executor for animation (builder.setAnimationExecutor will have no effect). In 4.10.0 this was fixed.

@rizwan321
Copy link

rizwan321 commented Aug 1, 2019

@yuriy-budiyev i am getting following error when update to 4.10.0
ERROR: Failed to resolve: com.github.bumptech.glide:glide:4.10.0

i have added following lines in build
implementation 'com.github.bumptech.glide:glide:4.10.0'
annotationProcessor 'com.github.bumptech.glide:compiler:4.10.0'

@yuriy-budiyev
Copy link
Contributor Author

yuriy-budiyev commented Aug 1, 2019

@sjudd Thread pool executor will increase it's pool size only if it's queue is full, oterwise it will stay on minimum possible thread count.

public static GlideExecutor newAnimationExecutor(
        int threadCount, UncaughtThrowableStrategy uncaughtThrowableStrategy) {
    return new GlideExecutor(
        new ThreadPoolExecutor(
            0 /* corePoolSize */,
            threadCount,
            KEEP_ALIVE_TIME_MS,
            TimeUnit.MILLISECONDS,
            new PriorityBlockingQueue<Runnable>(),
            new DefaultThreadFactory(
                ANIMATION_EXECUTOR_NAME,
                uncaughtThrowableStrategy,
                true)));
}

This pool will not work as expected because PriorityBlockingQueue isn't capacity constrained.

Executors.newCachedThreadPool uses SynchronousQueue which literally has no capacity and just transfers objects beetween producers and consumers. So each time when task is added and there are no available worker thread, it will create a new one.

PriorityBlockingQueue

/**
* Always returns {@code Integer.MAX_VALUE} because
* a {@code PriorityBlockingQueue} is not capacity constrained.
* @return {@code Integer.MAX_VALUE} always
*/
public int remainingCapacity() {
    return Integer.MAX_VALUE;
}

SynchronousQueue

/**
* Always returns zero.
* A {@code SynchronousQueue} has no internal capacity.
*
* @return zero
*/
public int remainingCapacity() {
    return 0;
}

SynchronousQueue is unusable for this situation because when maxPoolSize value will be reached it will cause producer to wait when cunsumer become available (all adding methods can block). Also, if you restrict PriorityBlockingQueue capacity, execution of the task will be rejected when the queue is full and maxPoolSize is reached.

So the possible solution is just use fixed thread pool for animations

public static GlideExecutor newAnimationExecutor(
        int threadCount, UncaughtThrowableStrategy uncaughtThrowableStrategy) {
    return new GlideExecutor(
        new ThreadPoolExecutor(
            threadCount,
            threadCount,
            0,
            TimeUnit.MILLISECONDS,
            new PriorityBlockingQueue<Runnable>(),
            new DefaultThreadFactory(
                ANIMATION_EXECUTOR_NAME,
                uncaughtThrowableStrategy,
                true)));
}

@yuriy-budiyev
Copy link
Contributor Author

yuriy-budiyev commented Aug 1, 2019

@rizwan321 See here.

@rizwan321
Copy link

@yuriy-budiyev thanks for the replying me
i have successfully updated build.gradle and using below implementations but again animation is slow.

Glide.with(context).load(icon).into(imageView);

can you please let me know what i have to add further and how to add ???

@rizwan321
Copy link

rizwan321 commented Aug 2, 2019

@yuriy-budiyev i also tried below lines but again animation is slow

GlideBuilder builder = new GlideBuilder(); builder.setAnimationExecutor(GlideExecutor.newSourceExecutor(DEFAULT_THREAD_POOL_SIZE, "animation", GlideExecutor.UncaughtThrowableStrategy.DEFAULT));
Glide.init(getApplicationContext(), builder.setMemoryCache(new LruResourceCache(1010241024)));

@yuriy-budiyev
Copy link
Contributor Author

yuriy-budiyev commented Aug 2, 2019

@rizwan321 See here. You should create your AppGlideModule, builder will be available in applyOptions method.

@rizwan321
Copy link

rizwan321 commented Aug 2, 2019

@yuriy-budiyev very sorry for disturbing you again and again.
still i am not been able to see desired result

i have implemented below in my application

@GlideModule
public final class MyAppGlideModule extends AppGlideModule {}

what do you mean by builder will be available in applyOptions method. ??

@yuriy-budiyev
Copy link
Contributor Author

@rizwan321 override this method in your class.

@yuriy-budiyev yuriy-budiyev changed the title GIF animations are very slow. Possible solution and workaraound. GIF animations are very slow. Possible solution and workaround. Aug 2, 2019
@rizwan321
Copy link

@yuriy-budiyev i am not getting your point what you are trying to say. It will be great if you paste a proper solution here ??

@yuriy-budiyev
Copy link
Contributor Author

yuriy-budiyev commented Aug 2, 2019

@rizwan321

@GlideModule
class MyGlideModule: AppGlideModule() {

    override fun applyOptions(
        context: Context,
        builder: GlideBuilder
    ) {
        builder.setAnimationExecutor(
            GlideExecutor.newSourceExecutor(
                4 /*thread count*/,
                "animation",
                GlideExecutor.UncaughtThrowableStrategy.DEFAULT
            )
        )
    }
}

@RISHABHPATNI1108
Copy link

Can anyone post a full detailed reply, I am still facing the issue, implemented snapshot library and made glide module class, how to solve the issue?

@rizwan321
Copy link

@yuriy-budiyev i have implemented glide with your suggested way. its speed is good in only Note7. while testing on other device Like S8, S7 its speed is slow as before. can you please look into it or let me know what i have to do further ?
waiting for your kind response.

@yuriy-budiyev
Copy link
Contributor Author

yuriy-budiyev commented Aug 23, 2019

@RISHABHPATNI1108 @rizwan321 Probably, better way is just wait 4.10.0 release. The snapshot version can be unstable and shouldn't be used in production. I tested my solution on Pixel 2 XL, Pixel 3, Galaxy S9, Galaxy Tab S3 and Redmi 5. Have you added glide compiler dependency? Read this article.

@sjudd sjudd closed this as completed Sep 18, 2019
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

4 participants