Communities

Writing
Writing
Codidact Meta
Codidact Meta
The Great Outdoors
The Great Outdoors
Photography & Video
Photography & Video
Scientific Speculation
Scientific Speculation
Cooking
Cooking
Electrical Engineering
Electrical Engineering
Judaism
Judaism
Languages & Linguistics
Languages & Linguistics
Software Development
Software Development
Mathematics
Mathematics
Christianity
Christianity
Code Golf
Code Golf
Music
Music
Physics
Physics
Linux Systems
Linux Systems
Power Users
Power Users
Tabletop RPGs
Tabletop RPGs
Community Proposals
Community Proposals
tag:snake search within a tag
answers:0 unanswered questions
user:xxxx search by author id
score:0.5 posts with 0.5+ score
"snake oil" exact phrase
votes:4 posts with 4+ votes
created:<1w created < 1 week ago
post_type:xxxx type of post
Search help
Notifications
Mark all as read See all your notifications »
Q&A

Welcome to Software Development on Codidact!

Will you help us build our independent community of developers helping developers? We're small and trying to grow. We welcome questions about all aspects of software development, from design to code to QA and more. Got questions? Got answers? Got code you'd like someone to review? Please join us.

Best Practices for Precalculating Expensive Variables in Functions

+1
−0

A class might calculate static variables during instantiation, which are then used by various functions. A common technique to optimize function performance is to precalculate expensive variables used by those functions.

However, this approach can lead to several issues:

  • It splits the function code across different places, making it harder to maintain.
  • It can cause the section where the variables are calculated to become crowded with seemingly unrelated code.
  • After editing one function, it is essential to review the entire class to determine if additional code needs to be adjusted. Related/dependent code isn't kept together.

What are the best practices for keeping the variables and functions together without sacrificing performance? I mean, if somebody edits a function or the variable calculation, they should be able to rapidly know which other code depends on it, so it can be fixed. That's easy if the precalculation code is together with the function, but the constructor of a class tends to initialize all the memoized variables, so it has a lot of orphan code, which actually belongs to the code of diverse functions written somewhere else.

I considered creating a class for each function, (such as PrecalculatingInitializer), and registering these classes in a common place. This way, an initializer function could call all registered PrecalculatingInitializer classes to memoize the necessary variables. However, this approach seems overly complicated and cumbersome. I don't even know if compilers support it.

I'm concerned about maintainability and performance.

History
Why does this post require attention from curators or moderators?
You might want to add some details to your flag.
Why should this post be closed?

0 comment threads

2 answers

You are accessing this answer with a direct link, so it's being shown above all other answers regardless of its score. You can return to the normal view.

+2
−0

If you want a best practice, I'm pretty sure the best practice is to do the straightforward and "naive" thing. From the perspective of OOP, your complaints are misguided. The unit of coherence isn't a function but a class. What's happening in the constructor should not be "unrelated code". Also, in the scenario you describe, there's exactly one method that would use the pre-initialized variable, and it's within the same class. A "Find All References" or even a basic string search should quickly find it. You don't need to painstakingly audit every line of the class' code, let alone any other code. Ideally, the class isn't so large that even reviewing everything would be very onerous, but, as just mentioned, that should be unnecessary.

After that, the next option would be to just have the function initialize the value on first execution. For example, you have an empty std::optional member that the function populates at the beginning if it is empty. You could make a little wrapper around this, similar to Rust's OnceCell, to improve safety and better communicate the intended usage. This approach would be beneficial if there is a decent likelihood that the expensive calculation may never need to be computed, and if performing the expensive calculation when the method is called is acceptable. This approach also does colocate the initialization code with the code that uses it, but you pay for the check every method invocation and there is a risk that some code will use the member before it's initialized. Admittedly, the latter should not be a significant concern in this use-case, and if you make a safe wrapper should be quickly caught.

Other approaches are unlikely to interact well with inheritance or seem idiomatic. Nevertheless, I will rationalize the approach you describe. The way this would typically be handled in a functional language is that you'd return a closure that closes over the precomputed value. For example, the following two functions in Haskell

myFunc1 = \x -> let expensive = ... in ... expensive ...
myFunc2 = let expensive = ... in \x -> ... expensive ...

have exactly the same type, but myFunc1 will compute expensive every time it's called, while myFunc2 is bound to a lambda that closes over expensive which will only be computed once. You can do the same thing in C++. Unfortunately, for the scenario you describe, you'd typically want to move expensive into the lambda, and not copy or reference it, so that it gets destroyed when the lambda does and isn't copied. This can be done in a reasonable way in C++14, but C++11 needs work-arounds. Of course, you could manually create the closure type which is essentially what your PrecalculatingInitializer would be. (Modulo the "registering" stuff you talk about. You can just have values of such types be members of the class. There's no need to "register" these in some "common place". Maybe this is what you meant by "registering".)

This approach will have some mildly awkward interactions with inheritance. You can implicitly (with lambdas) or explicitly pass in this to get access to member functions. If you use an explicit closure type, it will likely need to be declared a friend class, unless you nest it which is a reasonable thing to do in this case. A bigger issue is that you can't really override these closures. You could make a wrapper method that just delegates to the closure, and that wrapper method could be overridden. This would lead to wasted space storing the closure, but that is arguably about as bad as what would happen in the analogous situation for the other approaches.

In the very likely case that you don't actually care about inheritance here, the main issue with this last approach is that it is relatively verbose and not the most idiomatic code. It's quite verbose if you need to manually make closure types. If you use lambdas (modulo the C++11 limitations) it's not too bad, but you'd likely still want to have an "init" function for each closure. This wouldn't be much more verbose than the "naive" approach, but it would be a bit unusual looking and likely have some minor overheads.

History
Why does this post require attention from curators or moderators?
You might want to add some details to your flag.

0 comment threads

+0
−1

But checking if the variable has been calculated each time the function runs, wastes CPU cycles, which counteracts the purpose of precalculating the variables to save resources.

The PrecalculatingInitializer class was intended to wrap each function, keeping all related code together. It is automatically instantiated by the constructor of the class containing these functions. The PrecalculatingInitializer class initializes each instance it finds in a registry, such as an array, so it doesn't needs more code, like a function in a class does not needs more code to tell the class that it has a function. I don't know if that can be done without explicitly adding the class to a register, and It's probably a bad idea, but I'm asking if there is a better way.

History
Why does this post require attention from curators or moderators?
You might want to add some details to your flag.

0 comment threads

Sign up to answer this question »