Having a closer look at __do_visit in std::variant I grew curious about the performances of the std::variant polymorphic approach
I wrote a small test program to compare old school inheritance to the std::variant one
#include <variant>
#include <vector>
#include <iostream>
#include <string>
#include <chrono>
int i = 0;
// Polymorphism using variants
class circle
{
public:
void draw() const { i++; }
};
class line
{
public:
void draw() const { i++; }
};
using v_t = std::variant<circle, line>;
void variant_way(const std::vector<v_t>& v)
{
for (const auto &var : v)
std::visit([](const auto& o) {
o.draw();
}, var);
}
// old school
class shape
{
public:
virtual void draw() const = 0;
virtual ~shape() { }
};
class circle_in : public shape
{
public:
virtual void draw() const { i++; }
};
class line_in : public shape
{
public:
virtual void draw() const { i++; }
};
void inherit_way(const std::vector<shape*>& v)
{
for (const auto var : v)
var->draw();
}
// call and measure
template <typename F, typename D>
void run(F f, const D& data, std::string name)
{
auto start = std::chrono::high_resolution_clock::now();
f(data);
auto end = std::chrono::high_resolution_clock::now();
auto elapsed = std::chrono::duration_cast<std::chrono::microseconds>(end - start);
std::cout << name << ": "<< elapsed.count() << std::endl;
}
int main()
{
constexpr int howmany = 100000;
{
std::vector<v_t> v {howmany};
run(variant_way, v, "variant");
}
{
std::vector<shape*> v;
for (int i = 0; i < howmany; i++)
v.push_back(new circle_in());
run(inherit_way, v, "inherit_way");
// deallocate
}
return 0;
}
On my machine (i7, 16GB RAM), I get these results:
variant: 7487
inherit_way: 1302
I suspect that this result reflects the fact that the std::variant approach creates the vtable at each iteration while the inheriting approach does it once for all.
Is this explanation correct?
Is there a way to reduce the overhead?