chrisbanes / Android-BitmapCache

Android-BitmapCache is a specialised cache, for use with Android Bitmap objects.
834 stars 224 forks source link

put(Bitmap) is really slow #26

Closed pboos closed 11 years ago

pboos commented 11 years ago

I noticed that put seems to be pretty slow. So I did some tests:

53ms Connecting 525ms Downloading image 39ms Resizing image 191ms Saving resized image to sd card (manually) 969ms Put into cache (Android-BitmapCache)

So currently I am wondering WHAT is taking so long in the put method? It can't be saving the image to the SD card, since that only takes less than 1/4 of that time.

Any Ideas why that could be and how I could speed this up?

As well I looked at the speed when getting the image: 119ms Get image from cache (Android-BitmapCache) 80ms Decode bitmap from file manually

So I wonder what is taking those 40 additional milliseconds when getting the image.

Sorry for being so picky :). But this speed problem is currently the only reason why I am considering to writing my own cache. But I hope we can get this run faster :)

chrisbanes commented 11 years ago

Hi Patrick, can you post the code you use to create the cache?

chrisbanes commented 11 years ago

Also, can you post the code around the put() and get() calls?

pboos commented 11 years ago

Creating of cache:

File cacheLocation = new File(Environment.getExternalStorageDirectory()
                + "/Android/data/jp.co.cyberagent.httpsimple.sample.simple");
mBitmapCache = new BitmapLruCache.Builder(this)
                .setDiskCacheEnabled(true)
                .setDiskCacheLocation(cacheLocation)
                .setMemoryCacheEnabled(true)
                .setMemoryCacheMaxSizeUsingHeapSize(0.5f)
                .build();

Calling of get and put areas:

        private long mLastTimeStamp;
        private void logTime(String message) {
            if (mLastTimeStamp != 0) {
                Log.i("TEST", (System.currentTimeMillis() - mLastTimeStamp) + "ms " + message);
            }
            mLastTimeStamp = System.currentTimeMillis();
        }

        @Override
        public void run() {
            if (mCanceled) {
                return;
            }

            HttpSimple httpSimple = mContract.getHttpSimple();
            BitmapLruCache bitmapCache = mContract.getBitmapCache();

            logTime("Start");
            Drawable drawable = bitmapCache.get(mUrl);
            if (drawable != null) {
                show(drawable);
                logTime("Drawable from Cache");
                return;
            }

            try {
                // download
                mRequest = httpSimple.get(mUrl);
                InputStream inputStream = mRequest
                        .receiveProgress(this)
                        .inputStream();

                logTime("Connect to server");

                if (inputStream == null) {
                    return;
                }

                Bitmap bitmap = BitmapFactory.decodeStream(inputStream);
                if (bitmap == null) {
                    return;
                }

                logTime("Download/decode bitmap");

                // resize
                Bitmap resizedBitmap =
                        Bitmap.createScaledBitmap(bitmap, mImageSize, mImageSize, false);

                logTime("Resize Bitmap");

                resizedBitmap.compress(Bitmap.CompressFormat.JPEG, 100, new FileOutputStream(new File(Environment.getExternalStorageDirectory(), "test.png")));
                logTime("Compress as JPEG/100 to sd card (manually)");

                // cache
                // TODO this is slow
                drawable = bitmapCache.put(mUrl, resizedBitmap);
                logTime("Put into bitmapCache");

                // display
                show(drawable);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }

But I think I found the problem. compressing to PNG takes a lot longer than compressing to JPEG. In the above times I was compressing to JPEG. When I switched to compress to PNG (as you do), than I get about the same times.

Same applies by getting. I guess PNG is just not fast enough for big pictures (mine are around 1000x1000).

So would it be possible to switch to JPEG (maybe through some setting)? For my use case, that would speed up things a lot.

Times with PNG:

I/TEST    (10482): 334ms Connect to server
I/TEST    (10482): 954ms Download/decode bitmap
I/TEST    (10482): 111ms Resize Bitmap
I/TEST    (10482): 2199ms Compress as PNG/100 to sd card (manually)
I/TEST    (10482): 1986ms Put into bitmapCache

JPEG

I/TEST    (12746): 114ms Connect to server
I/TEST    (12746): 311ms Download/decode bitmap
I/TEST    (12746): 278ms Resize Bitmap
I/TEST    (12746): 911ms Compress as JPEG/100 to sd card (manually)
I/TEST    (12746): 3082ms Put into bitmapCache
chrisbanes commented 11 years ago

The commit above allows you to specify the compression format.

FYI, you can completely omit the encode and decode though by using the put(String, InputStream) or put(String, InputStream, BitmapFactory.Options) methods.

pboos commented 11 years ago

Thank you very much!

Yes, using the InputStream would be the best method to use. But images I am getting are not the correct size (sadly can not use a resizing image API service).

chrisbanes commented 11 years ago

You could use inSampleSize with the InputStream method ;)

pboos commented 11 years ago

I could, but it does not do the job :). Problem of inSampleSize are 2:

  1. It only allows me to reduce the size by factor 2. But to make android render nicely I need the exact size. ImageView scaling runs on UI thread which makes ListView/ViewPager pretty slow...
  2. To know the inSampleSize I need to use I need to run it once with inJustDecodeBounds. With a stream from the internet, that would be bad. Okay, I could quickly store it as a file and do it with a FileInputStream that i then as well give to BitmapCache.

For those reasons, the method you created seems to be the best for the task. Or do you have another idea? Thx a lot for a great library!

chrisbanes commented 11 years ago

Ah ok. I just want to make sure you were aware of this more efficient way.

Just to correct you a little bit, ImageView scaling works via draw matrices, which is actually efficient (especially on GPU accelerated canvases).

pboos commented 11 years ago

Thank you for the correction! Will have to try that out again. Last time I tried I think ListView had small stutters because of image scaling. Or maybe it was something else.

nikclayton commented 11 years ago

On 1 May 2013 12:27, Patrick Boos notifications@github.com wrote:

Thank you for the correction! Will have to try that out again. Last time I tried I think ListView had small stutters because of image scaling. Or maybe it was something else.

Might be related to https://plus.google.com/113058165720861374515/posts/iTk4PjgeAWX