Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Select an option

  • Save Luiz-Monad/89477b326f95621851690b7bdfb25838 to your computer and use it in GitHub Desktop.

Select an option

Save Luiz-Monad/89477b326f95621851690b7bdfb25838 to your computer and use it in GitHub Desktop.
"Mini Benchmark" on showcasing the performance overhead of different function-pointer techniques, as provided by C++11.Keep in mind, these are *bare* calls to functions that do only one thing, atomically incrementing its counter; By checking the generated assembler output you may find out, that a bare C-function pointer loop is compiled into 3 C…
// clang++ -o tests/perftest-fncb tests/perftest-fncb.cpp -std=c++11 -O3 -march=native -mtune=native
#include <algorithm>
#include <atomic>
#include <chrono>
#include <functional>
#include <stdio.h>
#include <string>
#include <thread>
#include <vector>
typedef unsigned long long counter_t;
struct Counter {
volatile counter_t c;
Counter() :
c(0)
{}
} counter;
void bare(Counter* counter) { __sync_fetch_and_add(&counter->c, 1); }
void ptr(Counter* counter) { __sync_fetch_and_add(&counter->c, 1); }
void cxx(Counter* counter) { __sync_fetch_and_add(&counter->c, 1); }
struct CXO1 {
void om(Counter* counter) { __sync_fetch_and_add(&counter->c, 1); }
virtual void omv(Counter* counter) { __sync_fetch_and_add(&counter->c, 1); }
void cxo1(Counter* counter) { __sync_fetch_and_add(&counter->c, 1); }
virtual void virt(Counter* counter) { __sync_fetch_and_add(&counter->c, 1); }
} cxo1;
void (*ptr_cb)(Counter*) = nullptr;
std::function<void(Counter*)> cxx_cb;
std::function<void(Counter*)> cxo1_cb;
std::function<void(Counter*)> virt_cb;
std::function<void(Counter*)> lambda_cb;
std::atomic<bool> run;
void bare_main() { while (run.load()) { bare(&counter); } }
void om_main() { while (run.load()) { cxo1.om(&counter); } }
void omv_main() { while (run.load()) { cxo1.omv(&counter); } }
void ptr_main() { while (run.load()) { ptr_cb(&counter); } }
void cxx_main() { while (run.load()) { cxx_cb(&counter); } }
void cxo1_main() { while (run.load()) { cxo1_cb(&counter); } }
void virt_main() { while (run.load()) { virt_cb(&counter); } }
void lambda_main() { while (run.load()) { lambda_cb(&counter); } }
static const char* labels[] = {
"bare function",
"object method",
"object method (virtual)",
"function pointer",
"C++11 std::function -> bare function",
"C++11 std::function -> object method",
"C++11 std::function -> object method (virtual)",
"C++11 std::function -> lambda",
};
struct Sample {
counter_t delta;
counter_t total;
size_t label;
};
int main()
{
ptr_cb = &ptr;
cxx_cb = std::bind(&cxx, std::placeholders::_1);
cxo1_cb = std::bind(&CXO1::cxo1, &cxo1, std::placeholders::_1);
virt_cb = std::bind(&CXO1::virt, &cxo1, std::placeholders::_1);
lambda_cb = [](Counter* c) { __sync_fetch_and_add(&c->c, 1); };
static const size_t NUM_COLS = 8;
static const size_t NUM_ROWS = 60;
std::vector<std::vector<Sample>> cols;
cols.reserve(NUM_COLS);
for (size_t test = 0; test < NUM_COLS; ++test) {
counter.c = 0;
cols.push_back({});
auto& rows = cols.back();
rows.reserve(NUM_ROWS);
run.store(true);
std::thread t;
switch (test) {
case 0: t = std::thread(bare_main); break;
case 1: t = std::thread(om_main); break;
case 2: t = std::thread(omv_main); break;
case 3: t = std::thread(ptr_main); break;
case 4: t = std::thread(cxx_main); break;
case 5: t = std::thread(cxo1_main); break;
case 6: t = std::thread(virt_main); break;
case 7: t = std::thread(lambda_main); break;
}
counter_t prev = 0;
for (size_t n = 0; n < NUM_ROWS; ++n) {
std::this_thread::sleep_for(std::chrono::seconds(1));
counter_t cur = counter.c;
rows.push_back({ cur - prev, cur, test });
prev = cur;
}
run.store(false);
t.join();
}
for (size_t n = 0; n < NUM_ROWS; ++n) {
std::vector<Sample> row;
row.reserve(NUM_COLS);
for (size_t col = 0; col < NUM_COLS; ++col)
row.push_back(cols[col][n]);
std::sort(row.begin(), row.end(),
[](const Sample& a, const Sample& b) {
return a.delta > b.delta;
});
printf("second %zu\n", n + 1);
printf("===========\n");
printf(" %16s %16s %s\n", "calls/sec", "total", "method");
printf(" %16s %16s %s\n", "---------", "-----", "------");
for (const auto& s : row)
printf(" %16llu %16llu %s\n", s.delta, s.total, labels[s.label]);
putchar('\n');
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment