0

In particular I am wondering if it is acceptable to have an instance variable that can be calculated by other instance variables (i.e. during the creation of the object)?

On one hand I see the data dependency and information redundancy as a possible issue. However on the other hand performance will suffer if the value of a costly calculation is not saved and has to be calculated with each access.

What is the best approach in such a scenario? Can memoization be used here?

1

4 Answers 4

5

However on the other hand performance will suffer if the value of a costly calculation is not saved and has to be calculated with each access.

Then save it. What you basically describe here - without using the exact term - is caching. There is nothing wrong with caching in general if you ensure that caching never serves outdated values.

if (!cachedValue) {
    cachedValue = expensiveCalculation(x, y, z);
}
return cachedValue;

You just need to make sure, that whenever either x, y, or z is altered, you clear the cached value, so it is recalculated next time it is fetched. And you need to ensure all of this is thread-safe, if your object shall be thread-safe.

Caching is the classical trade-off between memory and processing time. When writing code, you are quite often in a situation where you can speed up things (less processing time) by wasting more memory or save memory by slowing down things (more processing time). Of course, wasting more memory has other negative side effects, e.g. it limits how many objects you can have until you run out of memory, and so does wasting processing time, e.g. it limits how much work can be done within a given amount of time. Both can be solved though by getting more memory or processing power; buy more RAM or buy a faster CPU. In the end it depends what is cheaper: Memory or processing power?

Just avoid jumping to conclusions too quickly without actually profiling your code. Multiplying two integers on a modern CPU takes only one clock cycle if they are directly available to the CPU (in registers or first level cache), whereas fetching a value from memory takes also at least one clock cycle if the value is in a register or in first level cache but it can also take much longer (hundreds of clock cycles if it won't come from any CPU cache but main memory, already fetching from third level cache takes 75 clock cycles). So fetching a cached value from main memory can take much longer than calculating it from values already available to the CPU because they have recently been accessed. Don't assume that caching is faster for sure, measure real life scenarios to confirm that this really is the case.

1
  • If you have any doubts, don't cache, recalculate. Or make the object immutable. "There are only two hard things in Computer Science: cache invalidation and naming things." -- Phil Karlton
    – user949300
    May 15, 2021 at 22:20
1

On one hand I see the data dependency and information redundancy as a possible issue. However on the other hand performance will suffer if the value of a costly calculation is not saved and has to be calculated with each access.

There is no one-size-fits-all answer here. It depends on the cost of the calculation, the likelihood of needing the value, how often you're going to need the value, and how much you're trying to squeeze for performance (pro-caching) vs memory footprint (pro-recalculating).

Furthermore, the (im)mutability of the class very much influences how easy it is to figure out if any changes were made to the base data (which the calculated value relies on) and therefore if the value needs to be recalculated. Figuring that out can be a massive headache and is very easily avoided by simply always calculating the value without caching it.

There are plenty of cases where you don't need such a performance boost, and then I'd rather skip the code complexity and source for potential bugs, in favor of keeping things readable even if negligibly less performant.

If you are squeezing for performance though, lazy loading is always an option. The general idea here is to not pre-emptively calculate the value, only calculate it when requested, and from that point on remember the calculated value.

In C#, this can be done manually using properties:

public class MyClass
{
    private int? myValue;
    
    private int MyValue
    {
        get
        {
            if(myValue == null)
                myValue = VeryExpensiveCalculation();

            return myValue;
        }
    }

    private int VeryExpensiveCalculation()
    {
        return 1 + 1;
    }
}

This gets us the best of all behaviors:

  • If we never access MyValue, VeryExpensiveCalculation will never be run.
  • When we access MyValue, VeryExpensiveCalculation will be run.
  • When we access MyValue again, VeryExpensiveCalculation will not be run again, since we already did so before.

However, this approach has a few weak points:

  • You need a default value to indicate that the calculation has not been run yet. I used null to that end, but this might not work in your use case (when null is a meaningful value). This can be worked around using an extra boolean parameter, but it further increases code complexity and the potential for things to go wrong.
  • You're relying on the cooperation between a private field which stored the calculated value, and a public property which triggers the calculation of that value. But when you are dealing with logic inside the same class, you cannot hide the private field from sight. This makes it possible for developers to mistakenly use myValue instead of MyValue or even access VeryExpensiveCalculation directly, which bypasses all of the work we just put into this.

.NET has a Lazy<T> class which has made the above approach more reusable and easier to implement. This also helps cover some of the weak points.

public class MyClass
{
    public Lazy<int> MyLazyValue { get; private set; }

    public MyClass()
    {
        this.MyLazyValue = new Lazy<int>( () =>
        {
            return VeryExpensiveCalculation();
        }
    }

    private int VeryExpensiveCalculation()
    {
        return 1 + 1;
    }
}

This works the same way. VeryExpensiveCalculation will only be executed when you access MyLazyValue.Value, and it won't be executed when we access MyLazyValue.Value again.

There is still a bit of a technical cost though. You now have to access an additional nested property (Value). This can be worked around using custom properties, but then we run into the same weak point as before where private logic can bypass the custom property, on top of creating even more code for our implementation.

We also still suffer from the same problem where figuring out to perform a recalculation is not obvious.


To summarize, you can cache the calculated value to save on calculation performance, but be aware that this comes at the cost of increased code complexity and the possibility of bugs with knowing when (not) to recalculate the value, which means you should only implement this kind of caching when the benefit outweighs the cost in your particular situation.

0

It is not clear whether you need always to store calculated values or just sometimes and if the calculated values are always set at object creation time. In the easiest case one approach might simply be to use default values and multiple constructors. For example you might want to create a bank account with an initial balance or call another constructor with no balance value to set the default to 0:

class BankAccount { 

    String holderName;
    double balance;

    public BankAccount(String holderName, double balance) {
        this.holderName = holderName;
        this.balance = balance;
    }

    public BankAccount(String holderName) {
        this(holderName, 0);
    }

With the calculated value the approach could be the same, provide a value or use another constructor to calculate the default. The downside is that in this way you can only set the value once, the upside is that you don't have to add all the code to handle caching, checks for null values or eventual conditions to reset the values.

0

Caching with lazy loading only works if the dependencies are immutable. If they can change, then your choice is to recalculate the dependent value each time the dependencies change, or calculate it every time it's requested. If the dependencies change a lot compared to the number of requests for the dependent value, then you'll waste a lot of cycles running the expensive calculation to store a value that's never looked at.

In my experience, all of these considerations are detrimental 99% of the time. The best answer is usually to ignore performance and write the code in the simplest, easiest to understand and maintain way. One of the biggest causes of overly complicated systems that are an expensive nightmare to maintain and extend is premature optimization.

It's really easy to say to yourself, "Oh! This is a complicated and lengthy calculation, I need to code this system to avoid performing this calculation as much as possible." But the truth is that more often than not, this "expensive" calculation is only going to add some imperceptible lag to how long the system takes to respond to a user click.

Once in a while - or if you're one of the few people writing applications that perform real-time processing, or handling Big Data - you'll come across a situation where that expensive result is accessed 1000's of times in a loop, or needs to be available with virtually no latency. In that case, examine the specific usage profile, and optimize it appropriately.

The worst possible approach is follow that feeling that, "on the other hand performance will suffer...", and write complicated code that you don't need. And if you do it once, you'll do it again and again and again.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.