|
Got a question about Java? Java discussion forum
The cost of Java object orientation in mathematical calculations: optimisationsSo far, we have used a ComplexNumber class to encapsulate complex numbers in our calculation of a Newton's method fractal. We have also made the class immutable for maintainability. On this page, we consider two optimisations, both of which trade ease of coding and maintainability for improved performance:
Optimisation 1: A mutable version of ComplexNumberFirstly, then, we consider a mutable version of the ComplexNumber class: i.e. one whose components are not final, and where calling operator methods such as add(), multiply etc actually modifies the object it is called on rather than returning a new object. For example, the add method looks as follows: public ComplexNumber add(ComplexNumber other) { this.real += other.real; this.imag += other.imag; return this; } To make some of the calling code more succinct, we make each method return this so that we can chain methods together, but no new object is actually created: each number object's operator method always returns "itself". Notice that we now have to make a slight modification to the iterate() method: private void iterate() { ComplexNumber x = new ComplexNumber(this.xr, this.xi); iter = -1; for (int i = 0; i < 100; i++) { ComplexNumber num = (new ComplexNumber(x)).multiply(x).addReal(-4d); ComplexNumber denom = (new ComplexNumber(x)).multiplyReal(2d); ComplexNumber frac = num.divide(denom); x = x.subtract(frac); // ... as before ... Essentially, any time that we want to make a calculation based on a particular number but still retain the original value of that number, we need to remember to construct a new copy of that number. It's easy to see how we could introduce bugs by forgetting to do this! We should also note that our immutable class is no longer thread-safe without some extra provision. On the upside, all operator methods where we don't explicitly create a new ComplexNumber object will now re-use the same object, in principle improving performance. In a moment, we'll see just how much or how little it does improve performance in practice. Optimisation 2: eliminating ComplexNumberThe second optimisation we consider is to do away with the ComplexNumber class altogether. In effect, we declare local double variables to represent real and imaginary components as we need them, and "inline" the raw calculations (which were encapsulated in methods on ComplexNumber) as required. A possible implementation is as follows: private void iterate() { double xreal = this.xr; double ximag = this.xi; iter = -1; for (int i = 0; i < 100; i++) { // calculate (a+bi)/(c+di), where: // a+bi is x**2-4 // c+di is 2x double a = xreal * xreal - ximag * ximag - 4; double c = 2 * xreal; double b = c * ximag; double d = 2 * ximag; double divreal = (a * c + b * d) / (c * c + d * d); double divimag = (b * c - a * d) / (c * c + d * d); // The result of this division is the adjustment to x if (divreal == 0 && divimag == 0) { iter = i; break; } xreal -= divreal; ximag -= divimag; } this.xrResult = xreal; this.xiResult = ximag; } In this case, our expression x2-4 is about as simple as they get. It's easy to see how with more complex expressions, writing out the calculation code "by hand" could quickly become unweildy and prone to bugs. Nonetheless, there's no denying that from a performance point of view, this method has the advantage of eliminating all object management overhead. Performance of these optimisationsOn the next page, we discuss the performance gain from these optimisations. Copyright © Javamex UK 2010. All rights reserved. |