Skip to main content

[JAI-IMAGEIO] About the JPEG reading performance

17 replies [Last post]
Anonymous

Reply viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.
robert engels

I did some testing with djpeg on the same small image - it was about
17 ms - so ImageIO still have a way to go...

I can't test with JDK6 since I am on a Mac.

On Oct 2, 2007, at 7:49 PM, Fabrizio Giudici wrote:

>
> On Oct 3, 2007, at 02:34 , robert engels wrote:
>
>> i think there must be something wrong with your program... I used
>> the program below.
>>
>> On a dual core machine, when I ran with 1 thread, the avg time was
>> 708 ms, when I ran with 2 threads it was 390ms. Almost a linear
>> performance improvement by number or threads/cores.
>
> It's the kind of performance I expected. Thanks for this info, so
> now I know it's a problem of mine.
>
> Going back to the single core performance, I don't understand why
> the same image gets processed by djpeg in about 10ms, that is 10x
> faster (the Java 130 msec are 100msec for the decoding + 30msec for
> converting to a "compatible" image). Is it entirely due to the JNI
> overhead? Is there any parameter that I can set in reading (or in
> writing) to improve that?
>
> --
> Fabrizio Giudici, Ph.D. - Java Architect, Project Manager
> Tidalwave s.a.s. - "We make Java work. Everywhere."
> weblogs.java.net/blog/fabriziogiudici - www.tidalwave.it/blog
> Fabrizio.Giudici@tidalwave.it - mobile: +39 348.150.6941
>
>

[att1.html]

Fabrizio Giudici

So, here's the stuff. Thanks in advance to everybody.

The class under test is:

https://bluemarine.dev.java.net/svn/bluemarine/trunk/src/blueMarine-
core/Core/Thumbnail/src/it/tidalwave/bluemarine/thumbnail/impl/
ThumbnailImage.java

The test case and images can be downloaded here:

https://bluemarine.dev.java.net/svn/bluemarine/trunk/src/blueMarine-
core/Core/Thumbnail/test/unit/src/it/tidalwave/bluemarine/thumbnail/
impl/ThumbnailImageTest.java
https://bluemarine.dev.java.net/svn/bluemarine/trunk/src/blueMarine-
core/Core/Thumbnail/test/unit/src/it/tidalwave/bluemarine/thumbnail/
impl/image
https://bluemarine.dev.java.net/svn/bluemarine/trunk/src/blueMarine-
core/Core/Thumbnail/test/unit/src/it/tidalwave/bluemarine/thumbnail/
impl/log.properties
https://bluemarine.dev.java.net/svn/bluemarine/trunk/src/blueMarine-
core/Core/Thumbnail/test/unit/src/it/tidalwave/bluemarine/thumbnail/
impl/image/20060508-0145.jpg
https://bluemarine.dev.java.net/svn/bluemarine/trunk/src/blueMarine-
core/Core/Thumbnail/test/unit/src/it/tidalwave/bluemarine/thumbnail/
impl/image/thumbnail-620097.jpg
https://bluemarine.dev.java.net/svn/bluemarine/trunk/src/blueMarine-
core/Core/Thumbnail/test/unit/src/it/tidalwave/bluemarine/thumbnail/
impl/image/thumbnail.jpg

A few explanations about the code.

The ThumbnailImage is a holder for a BufferedImage designed for
efficiently managing LARGE batchs of thumbnails (100+ and maybe 1000
+). The main points are:

+ ThumbnailImage gets constructed with a File where the
representation of the thumbnail is stored (or will be stored). The
ThumbnailImage just created contains only its file reference, so
creating even 1000+ instances does not impact on memory.
+ when a ThumbnailImage is to be rendered, the AWT Thread which is
rendering the component calls getBufferedImage() and in a first time
this returns null; so a background thread is scheduled to call
loadImage() and refresh the rendering component later (this is not in
ThumbnailImage code)
+ when loadImage() is called, the File gets loaded in memory (once
for all) as an array of bytes (thanks to the JPEG compression, the
avg size is 20-40kB, which means 2-4MB for 100 items and 20-40MB for
1000 items). loadImage() then calls decompressImage() which decodes
the memory buffer into a BufferedImage, that is stored as a
SoftReference; in this was the GC can dispose it in case of
necessity. If the rendering component gets again a null from
getBufferedImage(), it reschedules loadImage() in background as earlier.
+ To be noted that decompressImage() converts the result in a
"compatible image" for having the best rendering speed (this means
taking a few msecs for rendering rather than 100+).

Summing up, you can put hundreds of instances in a grid renderer; the
memory allocation is a few MegaBytes; images are loaded on demand
(e.g. while the user scrolls the scrollbars and exposes items); only
the currently rendered images are decompressed, allocating more
memory; the load on demand doesn't require disk access thus should be
quick.

Given that, it's clear that the procedure to test is:

1. construct a ThumbnaiImage
2. invoke loadImage() - so the decoder is loaded the first time
3. invoke decompressImage() in a loop (100 times currently) and
measure the avg.

This is done by testDecompressSpeed(); testDecompressSpeedInParallel
() does the same on two images in parallel;
testDecompressSpeedOnOldImage() is the same as the first test, but
it's run on a JPEG with a different encoding.

These are the results on Mac OS X 10.4, JDK 1.5.0, Mac Book Pro 2GHz
(please reset everything I've said yesterday, now I double checked
everything):

Time to decompress a single image (msec): 80
Time to decompress an old image (msec): 135
Time to decompress a two images in PARALLEL (msec): 107 (vs 80, <--
good, clearly last night I did something wrong)

The "old image" is a thumbnail taken from an old version of my
application, but unfortunately we can't compare times to that, since
it's larger (400x400 instead of 400x266). But the time ratio (170%)
seems to be coherent with the pixel count ratio (150%). It is encoded
in a different way, though, since the "old" appears to have a good
RGB profile, while the "new" opened with Java has washed out colors
(which BTW is another problem, but another discussion thread); also,
the Mac OS X preview opens with no problems the "old", while the
"new" appears almost black.

In contrast, the time with djpeg for the "old image" is:

[Mistral:thumbnail/impl/image] fritz% time repeat 100 djpeg
thumbnail.jpg > /dev/null
0.837u 0.333s 0:01.22 95.0% 0+0k 0+0io 0pf+0w

looks like it's just 12 msecs. Unfortunately I can't test the new
image as I get

[Mistral:thumbnail/impl/image] fritz% djpeg
thumbnail-000004d1-0092.jpg > /dev/null
PPM output must be grayscale or RGB

which probably is related to the different, non RGB encoding.

With a profiler I measured that the conversion to a compatible image
takes 40msec. So the "pure" decompression speeds seems to be 40msec.
Way longer than the djpeg test!

Given that the multi core approach works, looks like I should get
50msec per thumbnail. Displaying 40 thumbnails would still require
50x40=2 sec, which is definitely too long. You know, you push a
button and see a "slow" response, that's the culprit for a fame of
"Java is slow". Now, my point is that I'd like to know:

1. what can be still improved or what I'm doing wrong. For instance,
an old message in the mailing list talks about the use of
ConvertColorOp that could be faster than the Graphics.drawImage()
approach for getting a compatible image; this could save a few msec?

2. once I know the limit that can't be pushed, I could implement some
alternate strategy. For instance, let's say my target is 500ms for
rendering my batch. At present time all thumbs are 400x400 and they
are resized on the fly, while drawing (it's fast). I could save them
in two sizes: e.g. 100x100 and 400x400 and use the most appropriate;
after all, if the user selects a large view, only a few thumbs can be
visible at the same time (e.g. 10) and my target per each thumb would
be 500/10=50ms, which sounds near to reach. Of course I'm hoping that
decompressing a 100x100 is much faster - my target would be
500/50=10msec. But at this point, the uncompressed size of each thumb
would be 100x100x3 = 30k, and I could probably go with an
uncompressed format (TIFF, PNG, what's the faster).

--
Fabrizio Giudici, Ph.D. - Java Architect, Project Manager
Tidalwave s.a.s. - "We make Java work. Everywhere."
weblogs.java.net/blog/fabriziogiudici - www.tidalwave.it/blog
Fabrizio.Giudici@tidalwave.it - mobile: +39 348.150.6941

[att1.html]

robert engels

Based on that last blog, it would appear that the JPEG reader in JDK
6 is almost twice as fast - which would get Java on par (if not
better) than similar Unix command line tools.

On Oct 2, 2007, at 7:49 PM, Fabrizio Giudici wrote:

>
> On Oct 3, 2007, at 02:34 , robert engels wrote:
>
>> i think there must be something wrong with your program... I used
>> the program below.
>>
>> On a dual core machine, when I ran with 1 thread, the avg time was
>> 708 ms, when I ran with 2 threads it was 390ms. Almost a linear
>> performance improvement by number or threads/cores.
>
> It's the kind of performance I expected. Thanks for this info, so
> now I know it's a problem of mine.
>
> Going back to the single core performance, I don't understand why
> the same image gets processed by djpeg in about 10ms, that is 10x
> faster (the Java 130 msec are 100msec for the decoding + 30msec for
> converting to a "compatible" image). Is it entirely due to the JNI
> overhead? Is there any parameter that I can set in reading (or in
> writing) to improve that?
>
> --
> Fabrizio Giudici, Ph.D. - Java Architect, Project Manager
> Tidalwave s.a.s. - "We make Java work. Everywhere."
> weblogs.java.net/blog/fabriziogiudici - www.tidalwave.it/blog
> Fabrizio.Giudici@tidalwave.it - mobile: +39 348.150.6941
>
>

[att1.html]

robert engels

Please email me the image - rengelsATtyler-eagleDOTcom and I'll run
some tests...

On Oct 2, 2007, at 7:49 PM, Fabrizio Giudici wrote:

>
> On Oct 3, 2007, at 02:34 , robert engels wrote:
>
>> i think there must be something wrong with your program... I used
>> the program below.
>>
>> On a dual core machine, when I ran with 1 thread, the avg time was
>> 708 ms, when I ran with 2 threads it was 390ms. Almost a linear
>> performance improvement by number or threads/cores.
>
> It's the kind of performance I expected. Thanks for this info, so
> now I know it's a problem of mine.
>
> Going back to the single core performance, I don't understand why
> the same image gets processed by djpeg in about 10ms, that is 10x
> faster (the Java 130 msec are 100msec for the decoding + 30msec for
> converting to a "compatible" image). Is it entirely due to the JNI
> overhead? Is there any parameter that I can set in reading (or in
> writing) to improve that?
>
> --
> Fabrizio Giudici, Ph.D. - Java Architect, Project Manager
> Tidalwave s.a.s. - "We make Java work. Everywhere."
> weblogs.java.net/blog/fabriziogiudici - www.tidalwave.it/blog
> Fabrizio.Giudici@tidalwave.it - mobile: +39 348.150.6941
>
>

[att1.html]

James Cheng

Hi Fabrizio,

> Going back to the single core performance, I don't understand why the
> same image gets processed by djpeg in about 10ms, that is 10x faster
> (the Java 130 msec are 100msec for the decoding + 30msec for converting
> to a "compatible" image). Is it entirely due to the JNI overhead? Is
> there any parameter that I can set in reading (or in writing) to improve
> that?

I wonder if you discarded the first read in your timing test. It might
take 100 ms for JVM to load a native library when it is needed the first
time. As Brian hinted, this overhead could be alleviated if the the
image were larger. I am not sure if the JVM can cache or pre-load a
native library. If not, then you might have to reuse the image reader
in order to get the speed close to that of the native library.

HTH,
-James

---------------------------------------------------------------------
To unsubscribe, e-mail: interest-unsubscribe@jai-imageio.dev.java.net
For additional commands, e-mail: interest-help@jai-imageio.dev.java.net

Fabrizio Giudici

On Oct 3, 2007, at 03:24 , James Cheng wrote:

> Hi Fabrizio,
>
>> Going back to the single core performance, I don't understand why
>> the same image gets processed by djpeg in about 10ms, that is 10x
>> faster (the Java 130 msec are 100msec for the decoding + 30msec
>> for converting to a "compatible" image). Is it entirely due to the
>> JNI overhead? Is there any parameter that I can set in reading (or
>> in writing) to improve that?
>
> I wonder if you discarded the first read in your timing test. It
> might

Yes, I did.

> take 100 ms for JVM to load a native library when it is needed the
> first
> time. As Brian hinted, this overhead could be alleviated if the the
> image were larger. I am not sure if the JVM can cache or pre-load a
> native library. If not, then you might have to reuse the image reader
> in order to get the speed close to that of the native library.

I'm using a reader pool for reusing the stuff. Today I'll do some
more precise testing (also try to shrink the code to a minimum for
sharing it), but I'm pretty sure that the performance for large JPEGs
are not so bad - I'm also using them for displaying full size images,
and I don't have problem in that area.

> Based on that last blog, it would appear that the JPEG reader in
> JDK 6 is almost twice as fast - which would get Java on par (if not
> better) than similar Unix command line tools.

Unfortunately I'm locked to Java 5 for development because of the Mac
(hopefully this will change by the end of the month), but I can run
Java 6 tests e.g. on Linux.

> The main difference I can see is that the JDK plugin does more
> color conversion, and there is a bug in the plugin that causes the
> native colorspace to not be used - this is a problem when the image
> is JPEG.JCS_YCbCr
>

Actually I see a different performance with another copy of the same
image, that is probably encoded differently (for instance, opening it
with the Mac OS X preview gives me a muddy thing). I'm aware of huge
performance issues with some special ICC profiles (non RGB), but I
didn't think of the JPEG encoding.

Anyway in the next email I'll send you the code and some samples.

--
Fabrizio Giudici, Ph.D. - Java Architect, Project Manager
Tidalwave s.a.s. - "We make Java work. Everywhere."
weblogs.java.net/blog/fabriziogiudici - www.tidalwave.it/blog
Fabrizio.Giudici@tidalwave.it - mobile: +39 348.150.6941

---------------------------------------------------------------------
To unsubscribe, e-mail: interest-unsubscribe@jai-imageio.dev.java.net
For additional commands, e-mail: interest-help@jai-imageio.dev.java.net

Brian Burkhalter

You mentioned thumbnails so is this comparison with 'djpeg' for small JPEGs?
What is the comparison for larger sizes, e.g., 2560 x 2048 x 3 or so?

> Going back to the single core performance, I don't understand why the same
> image gets processed by djpeg in about 10ms, that is 10x faster (the Java 130
> msec are 100msec for the decoding + 30msec for converting to a "compatible"
> image). Is it entirely due to the JNI overhead? Is there any parameter that I
> can set in reading (or in writing) to improve that?

>^..^< >^..^<

Brian Burkhalter
Java Media, Imaging, and Graphics
Sun Microsystems, Inc.

This email message is for the sole use of the intended recipient(s)
and may contain confidential and privileged information. Any
unauthorized review, use, disclosure or distribution is prohibited.
If you are not the intended recipient, please contact the sender by
reply email and destroy all copies of the original message.

---------------------------------------------------------------------
To unsubscribe, e-mail: interest-unsubscribe@jai-imageio.dev.java.net
For additional commands, e-mail: interest-help@jai-imageio.dev.java.net

robert engels

I did some more testing...

I changed the code to use Toolkit.createImage() and added a
MediaTracker to wait for the image to load.
It only took 19 ms per image... on par with djpeg. Using both cores,
the avg time was 8 ms.

Both classes use the same libjpeg native library.

The main difference I can see is that the JDK plugin does more color
conversion, and there is a bug in the plugin that causes the native
colorspace to not be used - this is a problem when the image is
JPEG.JCS_YCbCr

The problem arise because the getImageTypes() reads the header, and
creates the appropriate ImageTypeSpecifiers. In the course of this
the interval var iccCS is set. When the image is actually read, the
iccCS is recreated and thus the original ImageTypeSpecifier does not
match any (because it compares the colorspace instance), and so then
a color conversion is applied.

This is the code

} else if ((iccCS != null) &&
(cm.getNumComponents() == numComponents) &&
(cs != iccCS)) {
// We have an ICC profile but it isn't used in the dest
// image. So convert from the profile cs to the
target cs
convert = new ColorConvertOp(iccCS, cs, null);
// Leave IJG conversion in place; we still need it
} else if ((!cs.isCS_sRGB()) &&

It would seem that a big performance improvement could be obtained in
both impl by reading and returning more than 1 scan line at a time
(avoids the JNI overhead - there is a lot of GET/RELEASE on the
buffers/streams).

On Oct 2, 2007, at 7:49 PM, Fabrizio Giudici wrote:

>
> On Oct 3, 2007, at 02:34 , robert engels wrote:
>
>> i think there must be something wrong with your program... I used
>> the program below.
>>
>> On a dual core machine, when I ran with 1 thread, the avg time was
>> 708 ms, when I ran with 2 threads it was 390ms. Almost a linear
>> performance improvement by number or threads/cores.
>
> It's the kind of performance I expected. Thanks for this info, so
> now I know it's a problem of mine.
>
> Going back to the single core performance, I don't understand why
> the same image gets processed by djpeg in about 10ms, that is 10x
> faster (the Java 130 msec are 100msec for the decoding + 30msec for
> converting to a "compatible" image). Is it entirely due to the JNI
> overhead? Is there any parameter that I can set in reading (or in
> writing) to improve that?
>
> --
> Fabrizio Giudici, Ph.D. - Java Architect, Project Manager
> Tidalwave s.a.s. - "We make Java work. Everywhere."
> weblogs.java.net/blog/fabriziogiudici - www.tidalwave.it/blog
> Fabrizio.Giudici@tidalwave.it - mobile: +39 348.150.6941
>
>

[att1.html]

robert engels

You may also want to read this blog entry on ImageIO performance:

http://weblogs.java.net/blog/campbell/archive/
2006/01/400_horsepower.html

On Oct 2, 2007, at 7:49 PM, Fabrizio Giudici wrote:

>
> On Oct 3, 2007, at 02:34 , robert engels wrote:
>
>> i think there must be something wrong with your program... I used
>> the program below.
>>
>> On a dual core machine, when I ran with 1 thread, the avg time was
>> 708 ms, when I ran with 2 threads it was 390ms. Almost a linear
>> performance improvement by number or threads/cores.
>
> It's the kind of performance I expected. Thanks for this info, so
> now I know it's a problem of mine.
>
> Going back to the single core performance, I don't understand why
> the same image gets processed by djpeg in about 10ms, that is 10x
> faster (the Java 130 msec are 100msec for the decoding + 30msec for
> converting to a "compatible" image). Is it entirely due to the JNI
> overhead? Is there any parameter that I can set in reading (or in
> writing) to improve that?
>
> --
> Fabrizio Giudici, Ph.D. - Java Architect, Project Manager
> Tidalwave s.a.s. - "We make Java work. Everywhere."
> weblogs.java.net/blog/fabriziogiudici - www.tidalwave.it/blog
> Fabrizio.Giudici@tidalwave.it - mobile: +39 348.150.6941
>
>

[att1.html]

mjacob@union06.de

[att1.html]

Fabrizio Giudici

On Oct 3, 2007, at 02:34 , robert engels wrote:

> i think there must be something wrong with your program... I used
> the program below.
>
> On a dual core machine, when I ran with 1 thread, the avg time was
> 708 ms, when I ran with 2 threads it was 390ms. Almost a linear
> performance improvement by number or threads/cores.

It's the kind of performance I expected. Thanks for this info, so now
I know it's a problem of mine.

Going back to the single core performance, I don't understand why the
same image gets processed by djpeg in about 10ms, that is 10x faster
(the Java 130 msec are 100msec for the decoding + 30msec for
converting to a "compatible" image). Is it entirely due to the JNI
overhead? Is there any parameter that I can set in reading (or in
writing) to improve that?

--
Fabrizio Giudici, Ph.D. - Java Architect, Project Manager
Tidalwave s.a.s. - "We make Java work. Everywhere."
weblogs.java.net/blog/fabriziogiudici - www.tidalwave.it/blog
Fabrizio.Giudici@tidalwave.it - mobile: +39 348.150.6941

[att1.html]

robert engels

I used a smaller (approx. 40k) image and it was roughly 60 ms to
read. Using jpegtopnm it was about 40 ms.

In general I have found reading images using ImageIO to be faster
than most commercial applications - especially Windows Fax & Image
Viewer.

On Oct 2, 2007, at 7:49 PM, Fabrizio Giudici wrote:

>
> On Oct 3, 2007, at 02:34 , robert engels wrote:
>
>> i think there must be something wrong with your program... I used
>> the program below.
>>
>> On a dual core machine, when I ran with 1 thread, the avg time was
>> 708 ms, when I ran with 2 threads it was 390ms. Almost a linear
>> performance improvement by number or threads/cores.
>
> It's the kind of performance I expected. Thanks for this info, so
> now I know it's a problem of mine.
>
> Going back to the single core performance, I don't understand why
> the same image gets processed by djpeg in about 10ms, that is 10x
> faster (the Java 130 msec are 100msec for the decoding + 30msec for
> converting to a "compatible" image). Is it entirely due to the JNI
> overhead? Is there any parameter that I can set in reading (or in
> writing) to improve that?
>
> --
> Fabrizio Giudici, Ph.D. - Java Architect, Project Manager
> Tidalwave s.a.s. - "We make Java work. Everywhere."
> weblogs.java.net/blog/fabriziogiudici - www.tidalwave.it/blog
> Fabrizio.Giudici@tidalwave.it - mobile: +39 348.150.6941
>
>

[att1.html]

dmitri_don
Offline
Joined: 2005-07-06

Hi,

All your links require password, please let us know how to get to your files.

Fabrizio Giudici

On Oct 25, 2007, at 00:29 , jai-imageio@javadesktop.org wrote:

> Hi,
>
> All your links require password, please let us know how to get to
> your files.
> [Message sent by forum member 'dmitri_don' (dmitri_don)]
>
> http://forums.java.net/jive/thread.jspa?messageID=241987

If you're referring to some subversion link of mine, you should use --
username guest and an empt password.

--
Fabrizio Giudici, Ph.D. - Java Architect, Project Manager
Tidalwave s.a.s. - "We make Java work. Everywhere."
weblogs.java.net/blog/fabriziogiudici - www.tidalwave.it/blog
Fabrizio.Giudici@tidalwave.it - mobile: +39 348.150.6941

[att1.html]

robert engels

As my final analysis showed - just make sure the jpeg has the proper
color encoding and you will get speeds as fast a djpeg. Not really a
fair comparison since the 'fast" djpeg you are comparing to resultsin
a corrupted image...

I also think that you are probably chasing the dragon here - just too
many pixels to be decoded. This is why formats like MPEG only do the
deltas - otherwise they would never work.

You could rewrite it in C, and I think you would have the same problems.

On Oct 3, 2007, at 5:39 AM, Fabrizio Giudici wrote:

> So, here's the stuff. Thanks in advance to everybody.
>
> The class under test is:
>
> https://bluemarine.dev.java.net/svn/bluemarine/trunk/src/blueMarine-
> core/Core/Thumbnail/src/it/tidalwave/bluemarine/thumbnail/impl/
> ThumbnailImage.java
>
> The test case and images can be downloaded here:
>
> https://bluemarine.dev.java.net/svn/bluemarine/trunk/src/blueMarine-
> core/Core/Thumbnail/test/unit/src/it/tidalwave/bluemarine/thumbnail/
> impl/ThumbnailImageTest.java
> https://bluemarine.dev.java.net/svn/bluemarine/trunk/src/blueMarine-
> core/Core/Thumbnail/test/unit/src/it/tidalwave/bluemarine/thumbnail/
> impl/image
> https://bluemarine.dev.java.net/svn/bluemarine/trunk/src/blueMarine-
> core/Core/Thumbnail/test/unit/src/it/tidalwave/bluemarine/thumbnail/
> impl/log.properties
> https://bluemarine.dev.java.net/svn/bluemarine/trunk/src/blueMarine-
> core/Core/Thumbnail/test/unit/src/it/tidalwave/bluemarine/thumbnail/
> impl/image/20060508-0145.jpg
> https://bluemarine.dev.java.net/svn/bluemarine/trunk/src/blueMarine-
> core/Core/Thumbnail/test/unit/src/it/tidalwave/bluemarine/thumbnail/
> impl/image/thumbnail-620097.jpg
> https://bluemarine.dev.java.net/svn/bluemarine/trunk/src/blueMarine-
> core/Core/Thumbnail/test/unit/src/it/tidalwave/bluemarine/thumbnail/
> impl/image/thumbnail.jpg
>
>
> A few explanations about the code.
>
> The ThumbnailImage is a holder for a BufferedImage designed for
> efficiently managing LARGE batchs of thumbnails (100+ and maybe 1000
> +). The main points are:
>
> + ThumbnailImage gets constructed with a File where the
> representation of the thumbnail is stored (or will be stored). The
> ThumbnailImage just created contains only its file reference, so
> creating even 1000+ instances does not impact on memory.
> + when a ThumbnailImage is to be rendered, the AWT Thread which is
> rendering the component calls getBufferedImage() and in a first
> time this returns null; so a background thread is scheduled to call
> loadImage() and refresh the rendering component later (this is not
> in ThumbnailImage code)
> + when loadImage() is called, the File gets loaded in memory (once
> for all) as an array of bytes (thanks to the JPEG compression, the
> avg size is 20-40kB, which means 2-4MB for 100 items and 20-40MB
> for 1000 items). loadImage() then calls decompressImage() which
> decodes the memory buffer into a BufferedImage, that is stored as a
> SoftReference; in this was the GC can dispose it in case of
> necessity. If the rendering component gets again a null from
> getBufferedImage(), it reschedules loadImage() in background as
> earlier.
> + To be noted that decompressImage() converts the result in a
> "compatible image" for having the best rendering speed (this means
> taking a few msecs for rendering rather than 100+).
>
> Summing up, you can put hundreds of instances in a grid renderer;
> the memory allocation is a few MegaBytes; images are loaded on
> demand (e.g. while the user scrolls the scrollbars and exposes
> items); only the currently rendered images are decompressed,
> allocating more memory; the load on demand doesn't require disk
> access thus should be quick.
>
> Given that, it's clear that the procedure to test is:
>
> 1. construct a ThumbnaiImage
> 2. invoke loadImage() - so the decoder is loaded the first time
> 3. invoke decompressImage() in a loop (100 times currently) and
> measure the avg.
>
> This is done by testDecompressSpeed(); testDecompressSpeedInParallel
> () does the same on two images in parallel;
> testDecompressSpeedOnOldImage() is the same as the first test, but
> it's run on a JPEG with a different encoding.
>
> These are the results on Mac OS X 10.4, JDK 1.5.0, Mac Book Pro
> 2GHz (please reset everything I've said yesterday, now I double
> checked everything):
>
> Time to decompress a single image (msec): 80
> Time to decompress an old image (msec): 135
> Time to decompress a two images in PARALLEL (msec): 107 (vs 80, <--
> good, clearly last night I did something wrong)
>
> The "old image" is a thumbnail taken from an old version of my
> application, but unfortunately we can't compare times to that,
> since it's larger (400x400 instead of 400x266). But the time ratio
> (170%) seems to be coherent with the pixel count ratio (150%). It
> is encoded in a different way, though, since the "old" appears to
> have a good RGB profile, while the "new" opened with Java has
> washed out colors (which BTW is another problem, but another
> discussion thread); also, the Mac OS X preview opens with no
> problems the "old", while the "new" appears almost black.
>
> In contrast, the time with djpeg for the "old image" is:
>
> [Mistral:thumbnail/impl/image] fritz% time repeat 100 djpeg
> thumbnail.jpg > /dev/null
> 0.837u 0.333s 0:01.22 95.0% 0+0k 0+0io 0pf+0w
>
> looks like it's just 12 msecs. Unfortunately I can't test the new
> image as I get
>
> [Mistral:thumbnail/impl/image] fritz% djpeg
> thumbnail-000004d1-0092.jpg > /dev/null
> PPM output must be grayscale or RGB
>
> which probably is related to the different, non RGB encoding.
>
> With a profiler I measured that the conversion to a compatible
> image takes 40msec. So the "pure" decompression speeds seems to be
> 40msec. Way longer than the djpeg test!
>
> Given that the multi core approach works, looks like I should get
> 50msec per thumbnail. Displaying 40 thumbnails would still require
> 50x40=2 sec, which is definitely too long. You know, you push a
> button and see a "slow" response, that's the culprit for a fame of
> "Java is slow". Now, my point is that I'd like to know:
>
> 1. what can be still improved or what I'm doing wrong. For
> instance, an old message in the mailing list talks about the use of
> ConvertColorOp that could be faster than the Graphics.drawImage()
> approach for getting a compatible image; this could save a few msec?
>
> 2. once I know the limit that can't be pushed, I could implement
> some alternate strategy. For instance, let's say my target is 500ms
> for rendering my batch. At present time all thumbs are 400x400 and
> they are resized on the fly, while drawing (it's fast). I could
> save them in two sizes: e.g. 100x100 and 400x400 and use the most
> appropriate; after all, if the user selects a large view, only a
> few thumbs can be visible at the same time (e.g. 10) and my target
> per each thumb would be 500/10=50ms, which sounds near to reach. Of
> course I'm hoping that decompressing a 100x100 is much faster - my
> target would be 500/50=10msec. But at this point, the uncompressed
> size of each thumb would be 100x100x3 = 30k, and I could probably
> go with an uncompressed format (TIFF, PNG, what's the faster).
>
>
> --
> Fabrizio Giudici, Ph.D. - Java Architect, Project Manager
> Tidalwave s.a.s. - "We make Java work. Everywhere."
> weblogs.java.net/blog/fabriziogiudici - www.tidalwave.it/blog
> Fabrizio.Giudici@tidalwave.it - mobile: +39 348.150.6941
>
>

[att1.html]

Fabrizio Giudici

On Oct 3, 2007, at 12:39 , Fabrizio Giudici wrote:

>
> The test case and images can be downloaded here:
>
> https://bluemarine.dev.java.net/svn/bluemarine/trunk/src/blueMarine-
> core/Core/Thumbnail/test/unit/src/it/tidalwave/bluemarine/thumbnail/
> impl/ThumbnailImageTest.java
> https://bluemarine.dev.java.net/svn/bluemarine/trunk/src/blueMarine-
> core/Core/Thumbnail/test/unit/src/it/tidalwave/bluemarine/thumbnail/
> impl/image
> https://bluemarine.dev.java.net/svn/bluemarine/trunk/src/blueMarine-
> core/Core/Thumbnail/test/unit/src/it/tidalwave/bluemarine/thumbnail/
> impl/log.properties
> https://bluemarine.dev.java.net/svn/bluemarine/trunk/src/blueMarine-
> core/Core/Thumbnail/test/unit/src/it/tidalwave/bluemarine/thumbnail/
> impl/image/20060508-0145.jpg
> https://bluemarine.dev.java.net/svn/bluemarine/trunk/src/blueMarine-
> core/Core/Thumbnail/test/unit/src/it/tidalwave/bluemarine/thumbnail/
> impl/image/thumbnail-620097.jpg
> https://bluemarine.dev.java.net/svn/bluemarine/trunk/src/blueMarine-
> core/Core/Thumbnail/test/unit/src/it/tidalwave/bluemarine/thumbnail/
> impl/image/thumbnail.jpg

Also

https://bluemarine.dev.java.net/svn/bluemarine/trunk/src/blueMarine-
core/Core/Thumbnail/test/unit/src/it/tidalwave/bluemarine/thumbnail/
impl/image/thumbnail-000004d1-0092.jpg

--
Fabrizio Giudici, Ph.D. - Java Architect, Project Manager
Tidalwave s.a.s. - "We make Java work. Everywhere."
weblogs.java.net/blog/fabriziogiudici - www.tidalwave.it/blog
Fabrizio.Giudici@tidalwave.it - mobile: +39 348.150.6941

[att1.html]

robert engels

I did a quick test, by opening the image and then saving with another
program (to change the color model) then reading with ImageIO.

The avg time per image was 9 ms.

Seems like a bug in the JDK JPEG plugin as described in the earlier
email.

On Oct 3, 2007, at 1:59 AM, robert engels wrote:

> I did some more testing...
>
> I changed the code to use Toolkit.createImage() and added a
> MediaTracker to wait for the image to load.
> It only took 19 ms per image... on par with djpeg. Using both
> cores, the avg time was 8 ms.
>
> Both classes use the same libjpeg native library.
>
> The main difference I can see is that the JDK plugin does more
> color conversion, and there is a bug in the plugin that causes the
> native colorspace to not be used - this is a problem when the image
> is JPEG.JCS_YCbCr
>
> The problem arise because the getImageTypes() reads the header, and
> creates the appropriate ImageTypeSpecifiers. In the course of this
> the interval var iccCS is set. When the image is actually read, the
> iccCS is recreated and thus the original ImageTypeSpecifier does
> not match any (because it compares the colorspace instance), and so
> then a color conversion is applied.
>
> This is the code
>
> } else if ((iccCS != null) &&
> (cm.getNumComponents() == numComponents) &&
> (cs != iccCS)) {
> // We have an ICC profile but it isn't used in the
> dest
> // image. So convert from the profile cs to the
> target cs
> convert = new ColorConvertOp(iccCS, cs, null);
> // Leave IJG conversion in place; we still need it
> } else if ((!cs.isCS_sRGB()) &&
>
>
> It would seem that a big performance improvement could be obtained
> in both impl by reading and returning more than 1 scan line at a
> time (avoids the JNI overhead - there is a lot of GET/RELEASE on
> the buffers/streams).
>
> On Oct 2, 2007, at 7:49 PM, Fabrizio Giudici wrote:
>
>>
>> On Oct 3, 2007, at 02:34 , robert engels wrote:
>>
>>> i think there must be something wrong with your program... I used
>>> the program below.
>>>
>>> On a dual core machine, when I ran with 1 thread, the avg time
>>> was 708 ms, when I ran with 2 threads it was 390ms. Almost a
>>> linear performance improvement by number or threads/cores.
>>
>> It's the kind of performance I expected. Thanks for this info, so
>> now I know it's a problem of mine.
>>
>> Going back to the single core performance, I don't understand why
>> the same image gets processed by djpeg in about 10ms, that is 10x
>> faster (the Java 130 msec are 100msec for the decoding + 30msec
>> for converting to a "compatible" image). Is it entirely due to the
>> JNI overhead? Is there any parameter that I can set in reading (or
>> in writing) to improve that?
>>
>> --
>> Fabrizio Giudici, Ph.D. - Java Architect, Project Manager
>> Tidalwave s.a.s. - "We make Java work. Everywhere."
>> weblogs.java.net/blog/fabriziogiudici - www.tidalwave.it/blog
>> Fabrizio.Giudici@tidalwave.it - mobile: +39 348.150.6941
>>
>>
>

[att1.html]