Skip to main content

Graphics2D.fill threads locking

12 replies [Last post]
stephanenicoll
Offline
Joined: 2006-12-21
Points: 0

Hi,

I am currently profiling a server-side application which generates images based on a SVG document. The image has roughly 1500 points which are displayed using a symbol stored in a GIF image.

The profiling test uses 15 clients, requesting exactly the same image on the server.

JProfiler shows that the 15 threads are blocked most of the times. Further investigations show that they need a lock somewhere in Graphics2D.drawImage.

If we return only the SVG document to the client (i.e. we bypass the image generation) all goes well.

Does anyone already experienced this? Anyone has reccomendations or tips&tricks regarding the server-side generation of images in multithread?

Tested with JDK 1.4.2, 1.5 and 1.6

Thanks,
Stéphane

Reply viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.
axel_fix
Offline
Joined: 2010-02-17
Points: 0

Hi,

I'm using version 1.6.0_26, but the problem with parallel AA rendering seems to be still existing. I have to render a lot of arcs (>10^6). If AA is on, the rendering time GROWS UP if I render using 2 threads by factor 1/3! If I switch AA off, it works very good using every core by 100%.

Is there any solution now?

Thanks,
Axel

Jim Graham

The AA pipeline currently works by feeding the path to the renderer,
then looping along getting chunks of "graybits" coverage values for each
piece of the shape. Each chunk of "graybits" is then used to color the
destination surface.

Most of this code is reentrant, but one critical piece is not - the code
that creates a chunk of graybits from the digested path. Unfortunately,
the existing implementation (which is running on 8-10 years old now) was
written to use some fairly large internal data structures that were
considered too expensive to create fresh for each renderer/thread.
Fixing that may not be trivial, but we are evaluating right now if we
should replace the renderer with a newer renderer due to other issues so
we can keep multi-threading performance in mind during that evaluation
(and this may be another straw on the camel's back).

Please file an RFE on this with some of your stack traces...

...jim

java2d@javadesktop.org wrote:
> Looking into DuctusRenderer it has synchronized methods which might explain the locks.
>
> I guess the way we're using this is bad. How can help improve?
>
> Thanks,
>
> Stéphane
> [Message sent by forum member 'stephanenicoll' (stephanenicoll)]
>
> http://forums.java.net/jive/thread.jspa?messageID=188529
>
> ===========================================================================
> To unsubscribe, send email to listserv@java.sun.com and include in the body
> of the message "signoff JAVA2D-INTEREST". For general help, send email to
> listserv@java.sun.com and include in the body of the message "help".

===========================================================================
To unsubscribe, send email to listserv@java.sun.com and include in the body
of the message "signoff JAVA2D-INTEREST". For general help, send email to
listserv@java.sun.com and include in the body of the message "help".

stephanenicoll
Offline
Joined: 2006-12-21
Points: 0

> Please file an RFE on this with some of your stack traces...

Done. internal ID 875223.

Regards,
Stéphane

stephanenicoll
Offline
Joined: 2006-12-21
Points: 0

Thanks a lot for your help and your suggestion, I'll keep you posted.

I will file the RFE today.

Regards,
Stéphane

Jim Graham

Note that our non-AA renderers should be better able to multi-thread.
Another alternative would be to perform AA using over-sampling. If your
server has a decent amount of memory then it may be faster and provide
higher quality to render the image at a larger size and then use
interpolated scaling to reduce it down to the desired size.

For example - to use 2x2 over-sampling, you would use:

int scalebits = 1; // 1 bit == x2 oversampling
int scaledw = w << scalebits;
int scaledh = h << scalebits;
BufferedImage scaledbimg =
new BufferedImage(scaledw, scaledh, TYPE_INT_RGB);
Graphics2D g2d = scaledbimg.createGraphics();
g2d.scale(1< // Note - do not turn on AA...
render(g2d);
g2d.dispose();
BufferedImage bimg =
new BufferedImage(scaledw >> 1, scaledh >> 1,
TYPE_INT_RGB);
while (scalebits > 0) {
// Scale it N times by a factor of 0.5 at each step.
// If we scale it more than that on a given step we may
// lose some of the data due to the filtering.
g2d = bimg.createGraphics();
g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
RenderingHints.VALUE_INTERPOLATE_BILINEAR);
// Or use RenderingHints.VALUE_INTERPOLATE_BICUBIC, but
// BILINEAR should be fine here.
g2d.drawImage(scaledbimg,
0, 0, scaledw >> 1, scaledh >> 1,
0, 0, scaledw, scaledh, null);
g2d.dispose();
scaledw >>= 1;
scaledh >>= 1;
BufferedImage tmp = bimg;
bimg = scaledbimg;
scaledbimg = tmp;
}
return scaledbimg;
// Only use the topmost w,h of the image...or
// return scaledbimg.getSubImage(0, 0, w, h);
// INT_RGB (or INT_ARGB) formats are the best to use for the
// above operations.
// If you need to use a format other than INT_RGB, it is best
// to perform all of the above with INT_RGB images and then
// convert to the desired image format once at the end.

This uses 2 buffers since it isn't defined if scaling from one image to
itself is a valid operation. Also, the final image being returned may
be larger than the requested w,h. If you can control the subsequent
code so that it only uses the topmost wxh pixels then you are fine,
otherwise use BufferedImage.getSubImage() to trim it down. If the
server requests tend to be for images of a standard size, then each
rendering thread in the server could just create a single pair of
oversized images and reuse them for each request...

...jim

java2d@javadesktop.org wrote:
> Hi,
>
> I am currently profiling a server-side application which generates images based on a SVG document. The image has roughly 1500 points which are displayed using a symbol stored in a GIF image.
>
> The profiling test uses 15 clients, requesting exactly the same image on the server.
>
> JProfiler shows that the 15 threads are blocked most of the times. Further investigations show that they need a lock somewhere in Graphics2D.drawImage.
>
> If we return only the SVG document to the client (i.e. we bypass the image generation) all goes well.
>
> Does anyone already experienced this? Anyone has reccomendations or tips&tricks regarding the server-side generation of images in multithread?
>
> Tested with JDK 1.4.2, 1.5 and 1.6
>
> Thanks,
> Stéphane
> [Message sent by forum member 'stephanenicoll' (stephanenicoll)]
>
> http://forums.java.net/jive/thread.jspa?messageID=188043
>
> ===========================================================================
> To unsubscribe, send email to listserv@java.sun.com and include in the body
> of the message "signoff JAVA2D-INTEREST". For general help, send email to
> listserv@java.sun.com and include in the body of the message "help".

===========================================================================
To unsubscribe, send email to listserv@java.sun.com and include in the body
of the message "signoff JAVA2D-INTEREST". For general help, send email to
listserv@java.sun.com and include in the body of the message "help".

Chris Campbell

Hi Stéphane,

On which OS/platform is the server running? Is it running in
"headless" mode?

> The image has roughly 1500 points which are displayed using a
> symbol stored in a GIF image.

I'm confused by this statement. Are you saying that the original SVG
document is being converted into a GIF image on the server-side? How
large is the generated image in pixels?

How do you create the image that the SVG content is rendered into?
Is it something like "new BufferedImage()", or something else?

> JProfiler shows that the 15 threads are blocked most of the times.
> Further investigations show that they need a lock somewhere in
> Graphics2D.drawImage.

Do you have a stack trace or something else that shows which lock is
being used in the call stack? In your subject you mention
Graphics2D.fill(), but here you say Graphics2D.drawImage()...

These kinds of applications should scale well to many threads without
blocking, so the answers to the above questions may uncover some
problem in your application.

Thanks,
Chris

On Dec 21, 2006, at 6:15 AM, java2d@javadesktop.org wrote:
> Hi,
>
> I am currently profiling a server-side application which generates
> images based on a SVG document. The image has roughly 1500 points
> which are displayed using a symbol stored in a GIF image.
>
> The profiling test uses 15 clients, requesting exactly the same
> image on the server.
>
> JProfiler shows that the 15 threads are blocked most of the times.
> Further investigations show that they need a lock somewhere in
> Graphics2D.drawImage.
>
> If we return only the SVG document to the client (i.e. we bypass
> the image generation) all goes well.
>
> Does anyone already experienced this? Anyone has reccomendations or
> tips&tricks regarding the server-side generation of images in
> multithread?
>
> Tested with JDK 1.4.2, 1.5 and 1.6
>
> Thanks,
> Stéphane
> [Message sent by forum member 'stephanenicoll' (stephanenicoll)]
>
> http://forums.java.net/jive/thread.jspa?messageID=188043
>
> ======================================================================
> =====
> To unsubscribe, send email to listserv@java.sun.com and include in
> the body
> of the message "signoff JAVA2D-INTEREST". For general help, send
> email to
> listserv@java.sun.com and include in the body of the message "help".

===========================================================================
To unsubscribe, send email to listserv@java.sun.com and include in the body
of the message "signoff JAVA2D-INTEREST". For general help, send email to
listserv@java.sun.com and include in the body of the message "help".

stephanenicoll
Offline
Joined: 2006-12-21
Points: 0

> On which OS/platform is the server running?

Linux Debian (kernel 2.6-smp) and Gentoo (kernel 2.6-smp)

> Is it running in "headless" mode?

Yes (-Djava.awt.headless=true)

> The image has roughly 1500 points which are displayed using a
> symbol stored in a GIF image.

I'm confused by this statement. Are you saying that the original SVG
document is being converted into a GIF image on the server-side?

No, I'm wrong on this one. The application actually allows us to either output the data as an SVG document or as GIF/PNG/JPEG.

Basically, it's a GIS-based application. We retrieve points on a particular area that we want to "mark" with some graphics (for instance hotel, restaurant, whatever). In this case we have 1500 points on the generated image.

Images are generated on server-side so if we have 20 clients, we have 20 running threads generating images.

> How large is the generated image in pixels?

400*400

How do you create the image that the SVG content is rendered into?
Is it something like "new BufferedImage()", or something else?

I don't know. I am very new. I'll have more info tomorrow. The only thing which sounds a bit weird to me is that we call Graphic2D.fill a lot of time for a single image (~2500 times). I have also turn java2d debugging on and I can't see around 50.000 calls (native?)

> JProfiler shows that the 15 threads are blocked most of the times.
> Further investigations show that they need a lock somewhere in
> Graphics2D.drawImage.

Do you have a stack trace or something else that shows which lock is
being used in the call stack? In your subject you mention
Graphics2D.fill(), but here you say Graphics2D.drawImage()...

We have locks on both. JProfiler stacktrace (both the thread having the lock and the threads waiting for the lock) on Graphics2D.XXX. The monitor is java.lang.Class (?!). I am actually searching for a tool that would provide me more detail.

These kinds of applications should scale well to many threads without
blocking, so the answers to the above questions may uncover some
problem in your application.

I think so to. I am just unable to see where I should investigate.

Thanks a lot,

Stéphane

Brien Colwell

> JProfiler stacktrace (both the thread having the
> lock and the threads waiting for the lock) on Graphics2D.XXX. The
> monitor is java.lang.Class (?!). I am actually searching for a tool that
> would provide me more detail.

What detail are you looking for? Have you tried a thread dump
(ctrl-\ from the console the server is running in on Linux)?
The way that you obtain your Graphics instance would be useful to know.

Brien

java2d@JAVADESKTOP.ORG wrote: > On which OS/platform is the server running?

Linux Debian (kernel 2.6-smp) and Gentoo (kernel 2.6-smp)

> Is it running in "headless" mode?

Yes (-Djava.awt.headless=true)

> The image has roughly 1500 points which are displayed using a
> symbol stored in a GIF image.

I'm confused by this statement. Are you saying that the original SVG
document is being converted into a GIF image on the server-side?

No, I'm wrong on this one. The application actually allows us to either output the data as an SVG document or as GIF/PNG/JPEG.

Basically, it's a GIS-based application. We retrieve points on a particular area that we want to "mark" with some graphics (for instance hotel, restaurant, whatever). In this case we have 1500 points on the generated image.

Images are generated on server-side so if we have 20 clients, we have 20 running threads generating images.

> How large is the generated image in pixels?

400*400

How do you create the image that the SVG content is rendered into?
Is it something like "new BufferedImage()", or something else?

I don't know. I am very new. I'll have more info tomorrow. The only thing which sounds a bit weird to me is that we call Graphic2D.fill a lot of time for a single image (~2500 times). I have also turn java2d debugging on and I can't see around 50.000 calls (native?)

> JProfiler shows that the 15 threads are blocked most of the times.
> Further investigations show that they need a lock somewhere in
> Graphics2D.drawImage.

Do you have a stack trace or something else that shows which lock is
being used in the call stack? In your subject you mention
Graphics2D.fill(), but here you say Graphics2D.drawImage()...

We have locks on both. JProfiler stacktrace (both the thread having the lock and the threads waiting for the lock) on Graphics2D.XXX. The monitor is java.lang.Class (?!). I am actually searching for a tool that would provide me more detail.

These kinds of applications should scale well to many threads without
blocking, so the answers to the above questions may uncover some
problem in your application.

I think so to. I am just unable to see where I should investigate.

Thanks a lot,

Stéphane
[Message sent by forum member 'stephanenicoll' (stephanenicoll)]

http://forums.java.net/jive/thread.jspa?messageID=188239

===========================================================================
To unsubscribe, send email to listserv@java.sun.com and include in the body
of the message "signoff JAVA2D-INTEREST". For general help, send email to
listserv@java.sun.com and include in the body of the message "help".

__________________________________________________
Do You Yahoo!?
Tired of spam? Yahoo! Mail has the best spam protection around
http://mail.yahoo.com

===========================================================================
To unsubscribe, send email to listserv@java.sun.com and include in the body
of the message "signoff JAVA2D-INTEREST". For general help, send email to
listserv@java.sun.com and include in the body of the message "help".
[att1.html]

stephanenicoll
Offline
Joined: 2006-12-21
Points: 0

> The way that you obtain your Graphics instance would be useful to know.

bi = new BufferedImage(400,400,BufferedImage.TYPE_INT_ARGB);
Graphics2D context = bi.createContext();

The thread dumps confirm my blocking issue (no profiler is running on the server process)

"resin-tcp-connection-*:8080-25" daemon prio=10 tid=0x08bc9000 nid=0x4845 waiting for monitor entry [0x5c0ff000..0x5c0ffad0]
java.lang.Thread.State: BLOCKED (on object monitor)
at sun.java2d.pipe.DuctusRenderer.dropRasterizer(DuctusRenderer.java:55)
- waiting to lock <0x670aad28> (a java.lang.Class for sun.java2d.pipe.DuctusRenderer)
at sun.java2d.pipe.DuctusShapeRenderer.renderPath(DuctusShapeRenderer.java:109)
at sun.java2d.pipe.DuctusShapeRenderer.fill(DuctusShapeRenderer.java:49)
at sun.java2d.pipe.ValidatePipe.fill(ValidatePipe.java:142)
at sun.java2d.SunGraphics2D.fill(SunGraphics2D.java:2364)
at com.foo.bar.renderer.DocumentRenderer.internalDrawShape(DocumentRenderer.java:1843)

"resin-tcp-connection-*:8080-51" daemon prio=10 tid=0x5e3e4c00 nid=0x4843 waiting for monitor entry [0x5c3ff000..0x5c3ffbd0]
java.lang.Thread.State: BLOCKED (on object monitor)
at sun.java2d.pipe.DuctusRenderer.getAlphaTile(DuctusRenderer.java:62)
- waiting to lock <0x670aad28> (a java.lang.Class for sun.java2d.pipe.DuctusRenderer)
at sun.java2d.pipe.DuctusShapeRenderer.renderPath(DuctusShapeRenderer.java:77)
at sun.java2d.pipe.DuctusShapeRenderer.fill(DuctusShapeRenderer.java:49)
at sun.java2d.pipe.ValidatePipe.fill(ValidatePipe.java:142)
at sun.java2d.SunGraphics2D.fill(SunGraphics2D.java:2364)
at com.foo.bar.renderer.DocumentRenderer.internalDrawShape(DocumentRenderer.java:1843)

"resin-tcp-connection-*:8080-11" daemon prio=10 tid=0x5e69c400 nid=0x4834 waiting for monitor entry [0x5cafe000..0x5caff950]
java.lang.Thread.State: BLOCKED (on object monitor)
at sun.java2d.pipe.DuctusRenderer.getRasterizer(DuctusRenderer.java:45)
- waiting to lock <0x670aad28> (a java.lang.Class for sun.java2d.pipe.DuctusRenderer)
at sun.java2d.pipe.DuctusRenderer.createShapeRasterizer(DuctusRenderer.java:358)
at sun.java2d.pipe.DuctusShapeRenderer.renderPath(DuctusShapeRenderer.java:57)
at sun.java2d.pipe.DuctusShapeRenderer.fill(DuctusShapeRenderer.java:49)
at sun.java2d.pipe.ValidatePipe.fill(ValidatePipe.java:142)
at sun.java2d.SunGraphics2D.fill(SunGraphics2D.java:2364)
at com.foo.bar.renderer.DocumentRenderer.internalDrawShape(DocumentRenderer.java:1843)

"resin-tcp-connection-*:8080-53" daemon prio=10 tid=0x62e14400 nid=0x482e runnable [0x5d2fe000..0x5d2ffc50]
java.lang.Thread.State: RUNNABLE
at sun.java2d.loops.TransformHelper.Transform(Native Method)
at sun.java2d.pipe.DrawImage.renderImageXform(DrawImage.java:446)
at sun.java2d.pipe.DrawImage.transformImage(DrawImage.java:251)
at sun.java2d.pipe.DrawImage.copyImage(DrawImage.java:63)
at sun.java2d.pipe.DrawImage.copyImage(DrawImage.java:982)
at sun.java2d.pipe.ValidatePipe.copyImage(ValidatePipe.java:168)
at sun.java2d.SunGraphics2D.drawImage(SunGraphics2D.java:2979)
at sun.java2d.SunGraphics2D.drawImage(SunGraphics2D.java:3097)
at com.foo.bar.renderer.DocumentRenderer.internalDrawShape(DocumentRenderer.java:1946)

I can provide the full threads dump if necessary.

Does anyone has a clue?

Regards,
Stéphane

stephanenicoll
Offline
Joined: 2006-12-21
Points: 0

Looking into DuctusRenderer it has synchronized methods which might explain the locks.

I guess the way we're using this is bad. How can help improve?

Thanks,

Stéphane

stephanenicoll
Offline
Joined: 2006-12-21
Points: 0

anyone?

Brien Colwell

hi --

I wish I knew more about the j2d pipeline, but I'm under the impression that there is a single pipeline shared by all Graphics -- i.e. your issue is not specific to the Ductus pipeline. As a side note, it's my experience that the openGL pipeline offers the best performance when dealing with shapes and non-changing images (looks like what you have).

Even if it was supported by the pipeline, I'm not sure what you're trying to accomplish with parallel rendering. Wouldn't a first-come, first-serve architecture make more sense (e.g. a rendering queue)?

You may want to look at this article -- http://weblogs.java.net/blog/chet/archive/2004/08/threadaches.html

java2d@JAVADESKTOP.ORG wrote: anyone?
[Message sent by forum member 'stephanenicoll' (stephanenicoll)]

http://forums.java.net/jive/thread.jspa?messageID=189317

===========================================================================
To unsubscribe, send email to listserv@java.sun.com and include in the body
of the message "signoff JAVA2D-INTEREST". For general help, send email to
listserv@java.sun.com and include in the body of the message "help".

__________________________________________________
Do You Yahoo!?
Tired of spam? Yahoo! Mail has the best spam protection around
http://mail.yahoo.com

===========================================================================
To unsubscribe, send email to listserv@java.sun.com and include in the body
of the message "signoff JAVA2D-INTEREST". For general help, send email to
listserv@java.sun.com and include in the body of the message "help".
[att1.html]