I have an expensive for
loop that is taking more time than it should due to dynamic casting overhead inside the loop.
An example code would be the following (compilable)
#include <iostream>
#include <memory>
struct Base {
virtual ~Base() {}
};
struct DerivedA : Base {};
struct DerivedB : Base {};
struct Calculator {
virtual void proceed(const DerivedA& ) const {
std::cout << "doing A stuff" << std::endl;
}
virtual void proceed(const DerivedB& ) const {
std::cout << "doing B stuff" << std::endl;
}
};
void doStuff(const std::shared_ptr<Base> &base_ptr) {
Calculator calc;
// Code that does stuff using only Base properties
for(int i = 0; i < 1000000; i++) { // expensive loop
// "redundant" dynamic cast at every iteration
auto a_ptr = std::dynamic_pointer_cast<DerivedA>(base_ptr);
if(a_ptr) calc.proceed(*a_ptr);
auto b_ptr = std::dynamic_pointer_cast<DerivedB>(base_ptr);
if(b_ptr) calc.proceed(*b_ptr);
}
}
int main() {
std::shared_ptr<Base> base_ptr = std::make_shared<DerivedA>();
doStuff(base_ptr);
}
Since the class is not changing inside the function, I think that there has to be a way to avoid the polymorhpism overhead (and branching) inside the loop and perform a single dynamic cast and a single function call without having to write the loop multiple times.
What I considered:
proceed
call. I don't think that any of them solves the problem. Those are just different ways of doing the same thing.
I'm considering to rethink my design, but before that I would be happy to hear any ideas and suggestions that you may have to improve this piece of code.
First of all I would rather do as Baldrick suggests in a comment to the OP then I would try the other alternatives cited in the OP. I would everytime profiling/mesuring the results to make an informed decision.
If you are not yet satisfied, then I suggest something along these lines:
template <typename T>
void doStuffImpl(const T &obj) {
Calculator calc;
for(int i = 0; i < 1000000; i++)
calc.proceed(obj);
}
void doStuff(const std::shared_ptr<Base> &base_ptr) {
auto a_ptr = std::dynamic_pointer_cast<DerivedA>(base_ptr);
if (a_ptr)
doStuffImpl(*a_ptr);
auto b_ptr = std::dynamic_pointer_cast<DerivedB>(base_ptr);
if (b_ptr)
doStuffImpl(*b_ptr);
}