Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Caching method results in immutable objects

Suppose I have a simple interface representing a complex number, whose instances would be immutable. For the sake of brevity, I omitted the obvious plus, minus, times and divide methods that would simply create and return a new immutable instance.

public interface Complex {

    double real();

    double imaginary();

    double absolute();

    double angle();

}

Now the question is, what would the best way to implement this as an immutable class? The most simple and straightforward "I care about performance only when it's a problem" approach would be to store the real and imaginary parts as final fields and compute the absolute value and angle on every invocation of those methods. This keeps the class small and simple, but obviously the last two methods return the same result every time.

public final class NonCachingComplex implements Complex {

    private final double real;
    private final double imaginary;

    public NonCachingComplex(double real, double imaginary) {
        this.real = real;
        this.imaginary = imaginary;
    }

    @Override public double real() {
        return real;
    }

    @Override public double imaginary() {
        return imaginary;
    }

    @Override public double absolute() {
        return Math.sqrt((real * real) + (imaginary * imaginary));
    }

    @Override public double angle() {
        return absolute() == 0 ? 0 : (Math.acos(real / absolute()) * Math.signum(imaginary));
    }
}

So why not save the absolute value and angle into a field upon creation? Well, obviously the memory footprint of the class is now a bit larger and also, counting the the results for every instance created may be also contra productive if these two methods are called seldom.

public final class EagerCachingComplex implements Complex {

    private final double real;
    private final double imaginary;

    private final double absolute;
    private final double angle;

    public EagerCachingComplex(double real, double imaginary) {
        this.real = real;
        this.imaginary = imaginary;
        this.absolute = Math.sqrt((real * real) + (imaginary * imaginary));
        this.angle = absolute == 0 ? 0 : (Math.acos(real / absolute()) * Math.signum(imaginary));
    }

    // real() and imaginary() stay the same...

    @Override public double absolute() {
        return absolute;
    }

    @Override public double angle() {
        return angle;
    }
}

A third possibility I came up with is to compute the absolute value and angle lazily, on the first time they are required. But as you can see, this makes the code a bit cluttered and error prone. Also, I'm not sure if the use of volatile modifier is actually correct in this context.

public final class LazyCachingComplex implements Complex {

    private final double real;
    private final double imaginary;

    private volatile Double absolute;
    private volatile Double angle;

    public LazyCachingComplex(double real, double imaginary) {
        this.real = real;
        this.imaginary = imaginary;
    }

    // real() and imaginary() stay the same...

    @Override public double absolute() {
        if (absolute == null) {
            absolute = Math.sqrt((real * real) + (imaginary * imaginary));
        }
        return absolute;
    }

    @Override public double angle() {
        if (angle == null) {
            angle = absolute() == 0 ? 0 : (Math.acos(real / absolute()) * Math.signum(imaginary));
        }
        return angle;
    }

}

So my question is, which of these three approaches is the best? Is there some other even better approach? Should I care about performance at all and stay with the first approach and think about optimizations only when performance becomes a real problem?

like image 611
Natix Avatar asked Feb 22 '12 19:02

Natix


People also ask

What is immutable at objective?

In object-oriented and functional programming, an immutable object (unchangeable object) is an object whose state cannot be modified after it is created. This is in contrast to a mutable object (changeable object), which can be modified after it is created.

Can we use immutable objects in multithreading?

Immutable objects are useful in multithreaded applications because they can be safely accessed by several threads concurrently, without the need for locking or other synchronization.

What is immutable variable explain with example?

We can not change anything once the object is created. For example, primitive objects such as int, long, float, double, all legacy classes, Wrapper class, String class, etc. In a nutshell, immutable means unmodified or unchangeable. Once the immutable objects are created, its object values and state can not be changed.

What is immutable data in programming?

Immutable data is a piece of information in a database that cannot be (or shouldn't be) deleted or modified. Most traditional databases store data in a mutable format, meaning the database overwrites the older data when new data is available.


2 Answers

I'd go for NonCachingComplex pretty much every time.

Reasons:

  • It's the simplest - so you should write it this way first and only make things more complex if you prove that it is necessary through benchmarking. Avoid premature optimization and all that!
  • The formulae for calculating absolute() and angle() are probably not expensive enough to justify caching. Floating point operations on modern CPUs are very fast, often even faster than fetching a value from memory.
  • Lowest memory footprint - this is beneficial not only for reducing the overall memory consumption of your code, but it also improves performance because more of your data will fit in the higher-speed processor caches. This can make a big difference for certain working set sizes.

Of the others, LazyCachingComplex is particularly bad, since it is using boxed values for absolute and angle (which implies an extra memory dereference to access, plus two extra lots of object overhead). I think it unlikely that would ever see a performance benefit from doing this.

Note that if you really care about performance then you wouldn't use a Complex interface either - the best performance is from directly making a final Complex class, and referring to this class directly in your code. Method invocations via an interface are (slightly) more costly than method invocations on a final class.

like image 187
mikera Avatar answered Nov 04 '22 03:11

mikera


I'm sure it isn't the answer you are looking for, but I would say it depends ;)

I personally don't think the lazy init is bad at all. But I would weight your options against the needs of your application - is performance really a concern? Are you using the values of absolute and angle multiple times for the same object? Is the computation of those fields really that slow?

My rule of thumb is generally to keep the code as clean (minimal) as possible and only add complexity when it is shown that it is required. I would stick with your NonCaching version until then.

like image 28
Ryan Guill Avatar answered Nov 04 '22 05:11

Ryan Guill