Here are some guidelines based on common mistakes that I've encountered while correcting assignments for the COMP 557 Computer Graphics course. My hope is that other students will find it useful and avoid these pitfalls. The list below focuses on performance- something that is critical for many computer graphics applications- and many of these guidelines can be applied universally to Java programming. However, please use prudence when considering the guidelines below and strive to maintain a balance between performance and readability.
String
s wisely.
For example,
if( light.type.equals("AMBIENT") )
{
...
}
is better replaced by
if( light.type == Light.AMBIENT )
{
...
}
where in the latter case type
has an enumerated value defined in the Light
class.
String comparisons and other character operations soon add up, especially when used inside a display
method which is called many times a per second.
new
inside of a nested loop.
public Point3d computePosition() {
...
return new Point3d(x,y,z);
}
public void computePosition(Point3d pos) {
...
pos.set(x, y, z);
}
The first version does a lot of extra work: the object is created by allocating memory on the heap,
instance initializers are called, its superclass constructor method is called, the Point3d
constructor
method is called, plus the object must be tracked by the garbage collector! Math.pow(x,2)
to square a number?
It's far more efficient and clearer to simply use x*x
.Try running the following code
on your JVM to see the difference:
public static void main(String[] args) {
final int maxCount = 10000000;
{
double x = 10.0;
long start = System.nanoTime();
for(int i = 0; i < maxCount; ++i) {
double y = Math.pow(x, 2);
}
long stop = System.nanoTime();
System.out.println("Math.pow(x,2) seconds: " + (double)(stop-start)*1e-9);
}
vs.
{
double x = 10.0;
long start = System.nanoTime();
for(int i = 0; i < maxCount; ++i) {
double y = x*x;
}
long stop = System.nanoTime();
System.out.println("x*x seconds: " + (double)(stop-start)*1e-9);
}
}
On my laptop this produces:
Math.pow(x,2) seconds: 0.36674390100000004
x*x seconds: 0.012616440000000001
Ouch!
Now do this for a 1024x768 raytraced image with hundreds of objects and super-sampling and you get quite a perfomance hit.
Vector3d.scaleAdd
may have unintended results.Math.tan
!
Matrix4d.invert
for inverting rigid transform matrices should be avoided
since this method will perform a generalized inverse. Consider the following code:
{
Matrix4d tm1 = new Matrix4d();
tm1.setIdentity();
Matrix4d tm2 = new Matrix4d();
{
double x = 10.0;
long start = System.nanoTime();
for(int i = 0; i < maxCount; ++i) {
tm2.invert(tm1);
}
long stop = System.nanoTime();
System.out.println(" Matrix4d invert() seconds: " + (double)(stop-start)*1e-9);
}
}
vs.
{
Matrix4d tm1 = new Matrix4d();
tm1.setIdentity();
Matrix4d tm2 = new Matrix4d();
{
double x = 10.0;
long start = System.nanoTime();
for(int i = 0; i < maxCount; ++i) {
invertHomogeneous(tm1, tm2);
}
long stop = System.nanoTime();
System.out.println(" My custom invert seconds: " + (double)(stop-start)*1e-9);
}
}
On my laptop this produces:
Matrix4d invert() seconds: 7.310530138000001
Alternate invert seconds: 0.12186179500000001