Glide (with Cronet) using HTTP/3 takes much longer to download images than Glide using HTTP/1.1 or HTTP/2
- Dominant language
- Java
- Stars
- 35k
- Forks
- 6.2k
- Avg merge
- 1d 11h
- Merged PRs (30d)
- 8
Description
**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() {
@Override
public boolean onLoadFailed(@Nullable GlideException e, Object model, Target 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 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() {
@Override
public boolean onLoadFailed(@Nullable GlideException e, Object model, Target 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 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() {
@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() {
@Override
public boolean onLoadFailed(@Nullable GlideException e, Object model, Target 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 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
Contributor guide
Assessment
This issue has not been assessed yet.