Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

performance test - std::visitor compared with switch case

30 views
Skip to first unread message

Thiago Adams

unread,
Jun 29, 2020, 7:03:40 AM6/29/20
to

I did a performance test comparing a tagged union in C
against std::variant.
C was faster in my tests.

Here is the complete code for C and C++ in case someone wants
to check the C++ version.


#define MAXCOUNT 10000000

#ifndef __cplusplus


//C version

#include <assert.h>
#include <stdio.h>
#include <time.h>

struct circle {
double radius;
};
#define CIRCLE_TAG 1

struct square {
double side;
};
#define SQUARE_TAG 2

struct rectangle {
double width;
double height;
};
#define RECTANGLE_TAG 3

struct shape {
int tag;
union {
struct circle circle;
struct square square;
struct rectangle rectangle;
};
};

double shape_area(struct shape* s) {
switch (s->tag) {
case CIRCLE_TAG:
return 3.14 * s->circle.radius * s->circle.radius;
case SQUARE_TAG:
return s->square.side * s->square.side;
case RECTANGLE_TAG:
return s->rectangle.width * s->rectangle.height;
default:
assert(0);
break;
}
return 0;
}

int run_test(const char* message, int (*test)(void)) {
time_t start = clock();
int r = test();
printf("%s %d\n", message, (int)(clock() - start));
return r;
}

void TestC() {}

int main() {
struct shape s[] = {
{.tag = CIRCLE_TAG, .circle.radius = 5},
{.tag = SQUARE_TAG, .square.side = 2},
{.tag = RECTANGLE_TAG, .rectangle.width = 4, .rectangle.height = 5},
{.tag = CIRCLE_TAG, .circle.radius = 5},
{.tag = SQUARE_TAG, .square.side = 2},
{.tag = RECTANGLE_TAG, .rectangle.width = 4, .rectangle.height = 5},
{.tag = CIRCLE_TAG, .circle.radius = 5},
{.tag = SQUARE_TAG, .square.side = 2},
{.tag = RECTANGLE_TAG, .rectangle.width = 4, .rectangle.height = 5} };

double area = 0;
time_t start = clock();
for (int j = 0; j < MAXCOUNT; j++) {
area = 0;
for (int i = 0; i < sizeof(s) / sizeof(s[0]); i++) {
area += shape_area(&s[i]);
}
}
printf("%d\n", (int)(clock() - start));
printf("%f", area);

return 0;
}
#else

//C++ version

#include <ctime>
#include <iostream>
#include <memory>
#include <variant>
#include <vector>

struct circle {
circle(double const radius_) : radius(radius_) {}
double radius;
};

struct square {
square(double const side_) : side(side_) {}
double side;
};

struct rectangle {
rectangle(double width_, double height_)
: width(width_), height(height_) {}
double width;
double height;
};

double area(circle const& c) {
return 3.14 * c.radius * c.radius;
}
double area(square const& s) {
return s.side * s.side;
}
double area(rectangle const& r) {
return r.width * r.height;
}

using shape = std::variant<circle, square, rectangle>;

int main() {
shape shapes[] = { circle(5), square(2), rectangle(4, 5),
circle(5), square(2), rectangle(4, 5),
circle(5), square(2), rectangle(4, 5) };

double total_area = 0;

time_t start = clock();
for (int j = 0; j < MAXCOUNT; j++) {
total_area = 0;
for (int i = 0; i < sizeof(shapes) / sizeof(shapes[0]); i++) {
std::visit([&](auto const& shape) { total_area += area(shape); },
shapes[i]);
}
}
printf("time: %d\n\n", (int)(clock() - start));
std::cout << "Area total: " << total_area << std::endl;
return 0;
}

#endif

bol...@nowhere.co.uk

unread,
Jun 29, 2020, 7:11:01 AM6/29/20
to
On Mon, 29 Jun 2020 04:03:29 -0700 (PDT)
Thiago Adams <thiago...@gmail.com> wrote:
>I did a performance test comparing a tagged union in C
>against std::variant.

Hardly surprising - variant will have some internal check code that needs to
be run when its accessed whereas a union is simply a block of memory. Also
for some reason you're calling visit().

Also std::variant isn't a union anyway, its a multi-type single object
container. Unions allow silent casting from one type to another, variants
don't.


Öö Tiib

unread,
Jun 29, 2020, 10:08:13 AM6/29/20
to
On Monday, 29 June 2020 14:03:40 UTC+3, Thiago Adams wrote:
> I did a performance test comparing a tagged union in C
> against std::variant.
> C was faster in my tests.

By how lot?
Stateless lambdas are simpler when state is unneeded:

for (auto const& shape: shapes) {
total_area += std::visit([](auto const& s) { return area(s); }
, shape);
}

I don't know if it matters to performance here ... but it is easier
to read too.

Thiago Adams

unread,
Jun 29, 2020, 4:01:07 PM6/29/20
to
On Monday, June 29, 2020 at 11:08:13 AM UTC-3, Öö Tiib wrote:
> On Monday, 29 June 2020 14:03:40 UTC+3, Thiago Adams wrote:
> > I did a performance test comparing a tagged union in C
> > against std::variant.
> > C was faster in my tests.
>
> By how lot?

16% in my machine VC++2019


0 new messages