1

This question is generally regarding when it is a good idea to store quantities derived from member data in, say, std::vectors that will be repeatedly computed/accessed in various methods, but I will attempt to illustrate it within a specific context.

Suppose that I have two classes as minimally defined below. The Configuration class has multiple additional methods that need to access quantities derived from member data of Particle objects. For instance, in a simulation, the particle positions will be updated quite frequently, and these additional methods will need the separation vector between particles, computed with the computeSeparationVector(...) method below.

Is it better in terms of performance/efficiency to repeatedly call this function from the various methods that need this information, or is it more performant to compute this data once for each update step and store it in, say, std::vector<std::valarray<double>> separationVectors? If it's the latter, any suggestions on recommended ways to store such data would be greatly appreciated.

This is obviously a simplistic example, but deciding whether or not to store computed/derived data in member data containers is something I'm currently grappling with. Thanks!

#include <valarray>
#include <vector>

class Particle {
public:
    Particle(long index, double x, double y, double rad): 
        particleIndex(index),
        particleRadius(rad),
        xCoord(x),
        yCoord(y)
        { }

    const long particleIndex;
    double particleRadius;

    std::valarray<double> getCoords() {
        return {xCoord, yCoord};
    }

private:
    double xCoord;
    double yCoord;
};

class Configuration {
public:
    std::vector<Particle> particles;

    Particle getParticle(long particleIndex) const {
        return particles[particleIndex];
    }

    void addParticle(const Particle& particle) {
        particles.emplace_back(particle);
    }

    std::valarray<double> computeSeparationVector(long particle1Index, long particle2Index) const {
        return getParticle(particle1Index).getCoords() - getParticle(particle2Index).getCoords();
    }
};
  • 1
    The short answer is that nobody can tell you what's faster until you use it in code and profile it. Caching/memoization are well-known techniques for improving performance in cases like this. – user1118321 Nov 17 '17 at 3:00
  • @user1118321 Sure, I understand that there might be specific differences in performance, but are there general guidelines or considerations when it comes to this topic? (I guess I can try to look for discussions regarding memoization in C++.) – AnInquiringMind Nov 17 '17 at 4:06
7

The simplest code is generally the best, and repeatedly computing the result using code that mutates no memory is generally the simplest. Memoization is a technique you can use to increase performance at a stage when performance becomes an issue. If the inputs to the computation are immutable, it makes no difference if you compute and store the result, or compute each time, except in terms of performance. If the inputs are mutable (as in your case), the answer is more complex, as there may be only one "good" place in the code to update the cached result.

Typically the cached result is stored in the same object containing the inputs, which is the same object performing the computation. But there are many ways to do it. The computation object may be completely separate. Or you may wish to encapsulate the inputs, and only expose the computation results.

  • Thanks for the response. "Typically the cached result is stored in the same object containing the inputs, which is the same object performing the computation." Just to be sure, object here refers to a class object, right? In that case, the object would be an instance of the Configuration class (it contains the inputs and the member functions performing the computations), and so the cached result could be "stored in the same object" via a member data container such as the separationVectors vector I suggested in my question, right? – AnInquiringMind Nov 17 '17 at 4:16
  • @PhysicsCodingEnthusiast: Yes, that is right. – Bart van Ingen Schenau Nov 17 '17 at 8:08
  • Encapsulation, or an invariant between classes, would ensure the cached results are always synchronized with the mutable collection, when you retrieve them. This can be done with a dirty flag or by recomputing every time the collection is mutated. – Frank Hileman Nov 17 '17 at 17:27
  • @FrankHileman Could you expand a bit more on what you mean by "Encapsulation, or an invariant between classes, would ensure the cached results are always synchronized with the mutable collection, when you retrieve them."? I'm not entirely sure what encapsulation means in this context. Thanks! – AnInquiringMind Nov 17 '17 at 21:48
  • 1
    "Encapsulation" in my specific comment was referring to prohibiting users of the container from modifying the mutable collection directly; instead, they must use member methods, and each time a mutation occurs, an invalid flag is set on that container. Then when you retrieve the computation result, it is only recomputed if the invalid flag is set, otherwise the cached result is returned (this is basic design for cached results). The other option is to get the same result by invalidating a secondary object which is immutably bound, but accessible as a separate instance in the public API. – Frank Hileman Nov 18 '17 at 0:48

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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