I have two extremely simple toy implementations of an event loop, and would like to understand the performance differences between them.
First impl - events with a virtual 'handle' method - dynamic dispatch:
#include <memory>
#include <vector>
class Event {
public:
virtual bool handle() = 0;
};
class Event1 : public Event {
public:
bool handle() override {
return ++n < 1000000000;
}
private:
int n = 0;
};
class Event2 : public Event {
public:
bool handle() override {
--n;
return true;
}
private:
int n = 0;
};
class EventLoop {
public:
void addEvent(Event& event) {
events.push_back(&event);
}
void run() {
while (true) {
for (auto event : events) {
if (!event->handle()) {
return;
}
}
}
}
private:
std::vector<Event*> events;
};
int main() {
EventLoop eventLoop;
Event1 event1;
Event2 event2;
eventLoop.addEvent(event1);
eventLoop.addEvent(event2);
eventLoop.run();
return 0;
}
Second impl - wrap events in a lambda - static dispatch:
#include <vector>
#include <functional>
class Event1 {
public:
bool handle() {
return ++n < 1000000000;
}
private:
int n = 0;
};
class Event2 {
public:
bool handle() {
--n;
return true;
}
private:
int n = 0;
};
class EventLoop {
public:
template<typename T>
void addEvent(T& event) {
events.push_back([&event]() { return event.handle(); });
}
void run() {
while (true) {
for (auto& event : events) {
if (!event()) {
return;
}
}
}
}
private:
std::vector<std::function<bool()>> events;
};
int main() {
EventLoop eventLoop;
Event1 event1;
Event2 event2;
eventLoop.addEvent(event1);
eventLoop.addEvent(event2);
eventLoop.run();
return 0;
}
My naive expectation was that the version using lambdas would be faster (ie 'dynamic dispatch is slow'), but compiling with g++, it was twice as fast to run - 17s compared to 35s on my machine, according to time
.
I'd like to understand whether this is likely because with my toy event methods, the compiler was able to optimise the former better (and so in a more realistic scenario with more complex event handlers this would indeed typically be slower), or if there's some overhead associated with the lambdas that I don't understand that will likely always make this the case.
-O3
, I find the run time more or less the same for both (which is kinda expected). \$\endgroup\$