glide
glide copied to clipboard
Glide (with Cronet) using HTTP/3 takes much longer to download images than Glide using HTTP/1.1 or HTTP/2
Glide Version: 4.14.2
Integration libraries: play-services-cronet:18.0.1, cronet-integration:4.14.2
Device/Android Version: Galaxy A23/Android 13
Issue details / Repro steps / Use case background: I have a use case where I need to preload ~20-30 images.
I started out by using Glide without any integrations (ie HTTP/1.1) and it was taking (on average) 7.88s to complete this operation. Then I used Cronet and glide's cronet-integration without QUIC enabled (ie. HTTP/2) and it was taking (average) 7.95s to complete same operation. Then I used Cronet with QUIC enabled (ie. HTTP/3) and it took (average) 15.83s to complete the operation.
This was surprising. I was expecting HTTP/3 to be slightly better or at least equal to HTTP/2 and HTTP/1.1 but almost all times I downloaded images using HTTP/3 it took double time than HTTP/2 or HTTP/1.1
(I tried this exercise 10 times for each protocol version and cleared app data in between every trial).
Glide load line / GlideModule
(if any) / list Adapter code (if any):
Below are the code snippets I used for each of the versions -
Glide code with HTTP/1.1 -
// app gradle
implementation 'com.github.bumptech.glide:glide:4.14.2'
annotationProcessor 'com.github.bumptech.glide:compiler:4.14.2'
implementation 'jp.wasabeef:glide-transformations:4.3.0'
// code
mPreloadCounter = 0;
ConsoleLogger.d(TAG, "start fetching items");
long startTime = System.currentTimeMillis();
for (Item item : itemsMap.values()) {
Glide.with(getApplicationContext()).load(item.getURL()).listener(new RequestListener<Drawable>() {
@Override
public boolean onLoadFailed(@Nullable GlideException e, Object model, Target<Drawable> target, boolean isFirstResource) {
mPreloadCounter++;
if (mPreloadCounter == itemsMap.size()) {
ConsoleLogger.d(TAG, "end fetching items 1 " + (float)((System.currentTimeMillis() - startTime)/1000));
}
return false;
}
@Override
public boolean onResourceReady(Drawable resource, Object model, Target<Drawable> target, DataSource dataSource, boolean isFirstResource) {
mPreloadCounter++;
if (mPreloadCounter == itemsMap.size()) {
ConsoleLogger.d(TAG, "end fetching items 2 " + ((float)(System.currentTimeMillis() - startTime))/1000);
}
return false;
}
}).preload();
}
Glide code with HTTP/2 -
// app gradle
implementation 'com.github.bumptech.glide:glide:4.14.2'
implementation 'jp.wasabeef:glide-transformations:4.3.0'
implementation 'com.google.android.gms:play-services-cronet:18.0.1'
implementation "com.github.bumptech.glide:cronet-integration:4.14.2"
// code
mPreloadCounter = 0;
ConsoleLogger.d(TAG, "start fetching items");
long startTime = System.currentTimeMillis();
for (Item item : itemsMap.values()) {
Glide.with(getApplicationContext()).load(item.getURL()).listener(new RequestListener<Drawable>() {
@Override
public boolean onLoadFailed(@Nullable GlideException e, Object model, Target<Drawable> target, boolean isFirstResource) {
mPreloadCounter++;
if (mPreloadCounter == itemsMap.size()) {
ConsoleLogger.d(TAG, "end fetching items 1 " + (float)((System.currentTimeMillis() - startTime)/1000));
}
return false;
}
@Override
public boolean onResourceReady(Drawable resource, Object model, Target<Drawable> target, DataSource dataSource, boolean isFirstResource) {
mPreloadCounter++;
if (mPreloadCounter == itemsMap.size()) {
ConsoleLogger.d(TAG, "end fetching items 2 " + ((float)(System.currentTimeMillis() - startTime))/1000);
}
return false;
}
}).preload();
}
Glide code with HTTP/3 -
// app gradle
implementation 'com.github.bumptech.glide:glide:4.14.2'
implementation 'jp.wasabeef:glide-transformations:4.3.0'
implementation 'com.google.android.gms:play-services-cronet:18.0.1'
implementation "com.github.bumptech.glide:cronet-integration:4.14.2"
// code
CronetEngine.Builder builder = new CronetEngine.Builder(getApplicationContext());
builder.enableQuic(true);
builder.addQuicHint("cdn.domain.com", 443, 443);
CronetEngine cronetEngine = builder.build();
CronetRequestFactory factory = new CronetRequestFactoryImpl(
new Supplier<CronetEngine>() {
@Override
public CronetEngine get() {
return cronetEngine;
}
}
);
ChromiumUrlLoader.StreamFactory factory1 = new ChromiumUrlLoader.StreamFactory(factory, null);
Glide.get(getApplicationContext()).getRegistry().replace(GlideUrl.class, InputStream.class, factory1);
mPreloadCounter = 0;
ConsoleLogger.d(TAG, "start fetching items");
long startTime = System.currentTimeMillis();
for (Item item : itemsMap.values()) {
Glide.with(getApplicationContext()).load(item.getURL()).listener(new RequestListener<Drawable>() {
@Override
public boolean onLoadFailed(@Nullable GlideException e, Object model, Target<Drawable> target, boolean isFirstResource) {
mPreloadCounter++;
if (mPreloadCounter == itemsMap.size()) {
ConsoleLogger.d(TAG, "end fetching items 1 " + (float)((System.currentTimeMillis() - startTime)/1000));
}
return false;
}
@Override
public boolean onResourceReady(Drawable resource, Object model, Target<Drawable> target, DataSource dataSource, boolean isFirstResource) {
mPreloadCounter++;
if (mPreloadCounter == itemsMap.size()) {
ConsoleLogger.d(TAG, "end fetching items 2 " + ((float)(System.currentTimeMillis() - startTime))/1000);
}
return false;
}
}).preload();
}
In each of the 30 cases, none of the requests failed.
Layout XML: NA (only preloading images)
Stack trace / LogCat: NA (only success logs with latency)
Attaching screenshot of time taken (in seconds) every time for each of the versions.
I have read many articles online suggesting that HTTP/3 performs better than other versions in most of the use cases but here it was horrible. This makes me think that maybe problem is in my code.
Any suggestions how can I correctly/optimally use Glide with Cronet + QUIC?
PS: I know downloading 20-30 images concurrently is not ideal (can be broken down into parts) but I am curious about RCA of the behaviour that I observed today. Any insights are welcome.
Thanks