[OpenJDK 2D-Dev] sun.java2D.Pisces renderer Performance and Memory enhancements
Jim Graham
james.graham at oracle.com
Wed Apr 24 18:22:40 UTC 2013
On 4/24/13 1:59 AM, Laurent Bourgès wrote:
> Jim,
>
> First, here are both updated webrev and benchmark results:
> - results: http://jmmc.fr/~bourgesl/share/java2d-pisces/patch_opt_night.log
> - webrev: http://jmmc.fr/~bourgesl/share/java2d-pisces/webrev-2/
Some comments on the webrev:
- This caching of pipeline data in SG2D is a new precedent and I'm wary
of it for a couple of reasons:
- It gets tricky to satisfy all pipelines using that kind of technique
- It breaks encapsulation, but at least it is isolated to internal code
- SG2D will need to deal with the new field in clone().
- Rendering a hierarchy of Swing objects uses clone() a lot so
caching storage in SG2D may not help much in that case and thread local
may be more of a win
- Thread local storage doesn't really have those issues
- It's only used if that pipeline is used
- No encapsulation issues
- Don't need to modify clone() and it will work across clones
- Curve iterator uses auto-boxing, is that causing any churn?
- RenderingEngine may want to provide "forwarding methods" for Ductus as
a temporary solution until we fix Ductus to be aware of the new signatures
- new constants in Dasher were moved out of the class they are used from...?
- Why get rid of the final modifiers in Dasher? Was it not as effective
as copying to local variables? Note that the manual copying to local
variables is prone to maintenance issues if someone inserts a method
call in a block where the fields are stale.
- PRE.pathTo() could still be static, no? Also, it would be nice to
make this change to the existing RE helper method directly (and possibly
provide a backwards compatibility method with the old argument list that
simply forwards with a "new float[6]").
- Perhaps it is time to get rid of the try/catch pairs in PTG.getAlpha()
entirely?
- When you have a field cached in a local variable and you compute a new
value for it, then "field = var = ..." is probably better than "var =
field = ..." since the latter implies that the answer gets stored in the
field and then read back which is an extra memory operation. I noticed
this in a couple of places in Renderer, but I know I saw the local
variable caching in other files as well.
- A lot of undoing of some very carefully planned indentation and code
alignment issues. Auto-formatting is evil... ;)
- A personal rant - I'm a big fan of the { on a line by itself if it
follows an indented line-continued method declaration or control
statement. It helps the eye quickly find the start of the body because
it stands out. Your autoformatting got rid of a bunch of those and I
made a frowny face... :( (It may not be true to the JDK code style
guidelines, but we've been using that style throughout the 2D code for
years...)
- We switched to a new "within" technique in the JavaFX version of
Pisces based upon this paper:
http://www.cygnus-software.com/papers/comparingfloats/comparingfloats.htm
The C version of the implementation in that paper is:
// Usable AlmostEqual function
// See
http://www.cygnus-software.com/papers/comparingfloats/comparingfloats.htm
jboolean Helpers_withinULP(const jfloat A, const jfloat B, const int
maxUlps) {
// Make sure maxUlps is non-negative and small enough that the
// default NAN won't compare as equal to anything.
// assert(maxUlps > 0 && maxUlps < 4 * 1024 * 1024);
jint aInt, bInt;
// Make aInt lexicographically ordered as a twos-complement int
// This cast can induce "false positive" warnings from various
compilers
// or bug checking tools, but is correct as sizeof(jint) ==
sizeof(jfloat)
aInt = *((jint *) &A);
if (aInt < 0) {
aInt = 0x80000000 - aInt;
}
// Make bInt lexicographically ordered as a twos-complement int
// This cast can induce "false positive" warnings from various
compilers
// or bug checking tools, but is correct as sizeof(jint) ==
sizeof(jfloat)
bInt = *((jint *) &B);
if (bInt < 0) {
bInt = 0x80000000 - bInt;
}
// aInt,bInt are in the range [-0x7fffffff, +0x7fffffff]
// assuming maxUlps is much smaller than 0x7fffffff
// (<negative number> + maxUlps) will never overflow
// (<positive number> - maxUlps) will never overflow
if (aInt < bInt) {
return (aInt < 0) ? aInt + maxUlps >= bInt : bInt - maxUlps <=
aInt;
} else {
return (bInt < 0) ? bInt + maxUlps >= aInt : aInt - maxUlps <=
bInt;
}
}
It avoids multiple calls to ULP and the complexity of figuring out
whether to use the ULP of the larger or smaller number. The Java
version would have to use Float.floatToIntBits() where we used funny
pointer casting...
...jim
More information about the 2d-dev
mailing list