Skip to content

Instantly share code, notes, and snippets.

@flisboac
Last active September 21, 2016 10:22
Show Gist options
  • Save flisboac/61de67d3deb2c7cd23d0c63b7358197e to your computer and use it in GitHub Desktop.
Save flisboac/61de67d3deb2c7cd23d0c63b7358197e to your computer and use it in GitHub Desktop.
Testing different dynamic method dispatch techniques in C++11. Compile with `--std=c++11` or equivalent.
#include <iostream>
#include <string>
#include <sstream>
#include <vector>
#include <memory>
#include <functional>
#include <chrono>
#include <type_traits>
#include <thread>
#include <algorithm>
#include <thread>
#if 0
# Compile with this (save file as test-interface-method-dispatch.cpp):
compile_interface_method_dispatch() {
local basename="$(realpath "$(dirname "$1")")/$(basename "$1" | cut -d. -f1)";
g++ -DPROFILE -pg -o "$basename" "$1" && "$basename" > "${basename}.txt" && \
g++ -DDEBUG -g -o "$basename" "$1" && "$basename" >> "${basename}.txt" && \
g++ -DRELEASE -O3 -s -o "$basename" "$1" && "$basename" >> "${basename}.txt" && \
g++ -o "$basename" "$1" && "$basename" >> "${basename}.txt"
}
compile_interface_method_dispatch ~/test-interface-method-dispatch.cpp
#endif
#define UNKNOWN_BUILD_ID 0
#define RELEASE_BUILD_ID 1
#define DEBUG_BUILD_ID 2
#define PROFILE_BUILD_ID 3
#ifdef DEBUG
# define BUILD_TYPE DEBUG_BUILD_ID
# define BUILD_STR "debug"
#else
# ifdef PROFILE
# define BUILD_TYPE PROFILE_BUILD_ID
# define BUILD_STR "profile"
# else
# ifdef RELEASE
# define BUILD_TYPE RELEASE_BUILD_ID
# define BUILD_STR "release"
# else
# define BUILD_TYPE UNKNOWN_BUILD_ID
# define BUILD_STR "unknown"
# endif
# endif
#endif
#ifndef NTESTS
# define NTESTS 100000000
#endif
#define STRQT(T) #T
#define STRFY(T) STRQT(T)
struct Object {
void method() const {}
};
template <typename T> struct ClosureInterface {
ClosureInterface() : deref(nullptr) {}
ClosureInterface(std::function<T*(void)> _deref): deref(_deref) {}
ClosureInterface(T* _value) : deref([=]() -> T* { return _value; } ) {}
ClosureInterface(T& _value) : deref([&]() -> T* { return &_value; } ) {}
std::function<T*(void)> deref;
};
template <typename T> struct ClosureInterface<const T> {
ClosureInterface() : deref(nullptr) {}
ClosureInterface(std::function<T*(void)> _deref): deref(_deref) {}
ClosureInterface(const T* _value) : deref([=]() -> const T* { return _value; } ) {}
ClosureInterface(const T& _value) : deref([&]() -> const T* { return &_value; } ) {}
std::function<const T*(void)> deref;
};
template <typename T> class PointerInterface {
public:
typedef T* (*FDeref)(void*);
PointerInterface() : _fderef(defaultDeref), _pderef(nullptr) {}
PointerInterface(const T* _deref):
_fderef(defaultDeref),
_pderef(reinterpret_cast<void*>(_deref))
{}
PointerInterface(const T& _deref):
_fderef(defaultDeref),
_pderef(reinterpret_cast<void*>(&_deref))
{}
inline T* deref() const { return _fderef(_pderef); }
private:
static inline T* defaultDeref(void* pderef)
{ return (void*)(pderef); }
FDeref _fderef;
void* _pderef;
};
template <typename T> class PointerInterface<const T> {
public:
typedef const T* (*FDeref)(void*);
PointerInterface() : _fderef(defaultDeref), _pderef(nullptr) {}
PointerInterface(const T* _deref):
_fderef(defaultDeref),
_pderef(const_cast<void*>(reinterpret_cast<const void*>(_deref)))
{}
PointerInterface(const T& _deref):
_fderef(defaultDeref),
_pderef(const_cast<void*>(reinterpret_cast<const void*>(&_deref)))
{}
PointerInterface(FDeref fderef, void* pderef) : _fderef(fderef), _pderef(pderef) {}
inline const T* deref() const { return _fderef(_pderef); }
private:
static inline const T* defaultDeref(void* pderef)
{ return const_cast<const T*>(reinterpret_cast<T*>(pderef)); }
FDeref _fderef;
void* _pderef;
};
template <typename T> struct IInheritanceInterface {
virtual ~IInheritanceInterface() {}
virtual T* deref() const = 0;
};
template <typename T> struct IInheritanceInterface<const T> {
virtual ~IInheritanceInterface() {}
virtual const T* deref() const = 0;
};
template <typename T> class InheritanceInterface : public IInheritanceInterface<T> {
public:
InheritanceInterface() : value_(nullptr) {}
InheritanceInterface(T* _value) : value_(_value) {}
InheritanceInterface(T& _value) : value_(&_value) {}
inline T* deref() const { return value_; }
private:
T* value_;
};
template <typename T> class InheritanceInterface<const T> : public IInheritanceInterface<const T> {
public:
InheritanceInterface() : value_(nullptr) {}
InheritanceInterface(const T* _value) : value_(_value) {}
InheritanceInterface(const T& _value) : value_(&_value) {}
inline const T* deref() const { return value_; }
private:
const T* value_;
};
template <
template <typename TFT> typename F,
typename T
>
class Facade {
public:
Facade() : _interface(nullptr) {}
Facade(F<T>* interface) : _interface(interface) {}
Facade(F<T>& interface) : _interface(&interface) {}
Facade(const Facade<F, T>& rhs) = delete;
Facade(Facade<F, T>&& rhs) = default;
~Facade() { delete _interface; }
inline T* operator->() const { return _interface->deref(); }
inline T& operator*() const { return *_interface->deref(); }
private:
F<T>* _interface;
};
template <
template <typename TFT> typename F,
typename T
>
class Facade<F, const T> {
public:
Facade() : _interface(nullptr) {}
Facade(F<const T>* interface) : _interface(interface) {}
Facade(F<const T>& interface) : _interface(&interface) {}
Facade(const Facade<F, const T>& rhs) = delete;
Facade(Facade<F, const T>&& rhs) = default;
~Facade() { delete _interface; }
inline const T* operator->() const { return _interface->deref(); }
inline const T& operator*() const { return *_interface->deref(); }
private:
F<const T>* _interface;
};
template<
template <typename TFT> typename F,
typename T = Object
>
static inline Facade<F, T> makeFacade(T& object) {
return Facade<F, T>(new F<T>(object));
}
template <typename ID, ID InvalidValue>
class BasicNullableEnumValue_ {
public:
const ID id = InvalidValue;
constexpr BasicNullableEnumValue_() = default;
constexpr BasicNullableEnumValue_(const BasicNullableEnumValue_& rhs) = default;
constexpr BasicNullableEnumValue_(BasicNullableEnumValue_&& rhs) = default;
BasicNullableEnumValue_& operator=(const BasicNullableEnumValue_& rhs) = default;
BasicNullableEnumValue_& operator=(BasicNullableEnumValue_&& rhs) = default;
inline operator bool() const { return id == InvalidValue; }
inline bool operator==(const BasicNullableEnumValue_& rhs) const {
const bool thisValid = *this, rhsValid = rhs;
return (!thisValid && !rhsValid) || (thisValid && rhsValid && id == rhs.id);
}
inline bool operator!=(const BasicNullableEnumValue_& rhs) const { return !(*this == rhs); }
inline bool operator<(const BasicNullableEnumValue_& rhs) const { return id < rhs.id; }
protected:
constexpr BasicNullableEnumValue_(ID id_):
id(id_) {}
};
enum class ETestType {
Unknown,
Raw,
Closure,
Pointer,
Inheritance
};
template <ETestType type = ETestType::Unknown>
class TestType {
};
template <>
struct TestType<ETestType::Raw> {
constexpr static const ETestType id = ETestType::Raw;
constexpr static const char repr = 'r';
constexpr static const char *const name = "raw";
};
template <>
struct TestType<ETestType::Closure> {
constexpr static const ETestType id = ETestType::Closure;
constexpr static const char repr = 'c';
constexpr static const char *const name = "closure";
};
template <>
struct TestType<ETestType::Pointer> {
constexpr static const ETestType id = ETestType::Pointer;
constexpr static const char repr = 'p';
constexpr static const char *const name = "pointer";
};
template <>
struct TestType<ETestType::Inheritance> {
constexpr static const ETestType id = ETestType::Inheritance;
constexpr static const char repr = 'i';
constexpr static const char *const name = "inheritance";
};
template <>
class TestType<ETestType::Unknown> {
public:
constexpr static const ETestType id = ETestType::Unknown;
constexpr static const char repr = '?';
constexpr static const char *const name = "unknown";
class Value : public BasicNullableEnumValue_<ETestType, ETestType::Unknown> {
public:
friend class TestType<ETestType::Unknown>;
const char repr = '\0';
const char *const name = nullptr;
constexpr Value() = default;
private:
constexpr Value(ETestType id_, char repr_, const char *const name_):
BasicNullableEnumValue_<ETestType, ETestType::Unknown>::BasicNullableEnumValue_(id_),
repr(repr_),
name(name_) {}
};
constexpr static inline std::array<ETestType, 5> ids() {
return {
ETestType::Unknown,
ETestType::Raw,
ETestType::Closure,
ETestType::Pointer,
ETestType::Inheritance
};
}
constexpr static inline Value null_value()
{ return Value(); }
template <ETestType t = ETestType::Unknown>
constexpr static inline Value value() {
return Value(
TestType<t>::id, TestType<t>::repr, TestType<t>::name
);
}
constexpr static inline Value value(ETestType t) {
switch(t) {
case ETestType::Raw: return value<ETestType::Raw>();
case ETestType::Closure: return value<ETestType::Closure>();
case ETestType::Pointer: return value<ETestType::Pointer>();
case ETestType::Inheritance: return value<ETestType::Inheritance>();
}
return TestType<ETestType::Unknown>::value();
}
};
template <
template <typename TFT> typename F
>
struct DeductTestType {
};
template <>
struct DeductTestType<ClosureInterface> {
constexpr static ETestType id = ETestType::Closure;
using type = TestType<ETestType::Closure>;
};
template <>
struct DeductTestType<InheritanceInterface> {
constexpr static ETestType id = ETestType::Inheritance;
using type = TestType<ETestType::Inheritance>;
};
template <>
struct DeductTestType<PointerInterface> {
constexpr static ETestType id = ETestType::Pointer;
using type = TestType<ETestType::Pointer>;
};
class DispatchTest {
public:
friend class std::less<DispatchTest>;
using Clock = std::conditional<
std::chrono::high_resolution_clock::is_steady,
std::chrono::high_resolution_clock,
std::chrono::steady_clock
>::type;
using TimePoint = Clock::time_point;
DispatchTest() : _ntests(NTESTS) {}
DispatchTest(unsigned long ntests_) : _ntests(ntests_) {}
DispatchTest(const DispatchTest& rhs) = default;
DispatchTest(DispatchTest&& rhs) = default;
DispatchTest& operator=(const DispatchTest& rhs) = default;
DispatchTest& test(
std::function<void(void)> subject,
ETestType type = ETestType::Unknown
) {
_type = type;
_startTime = Clock::now();
for (unsigned long i = 0; i < _ntests; ++i)
subject();
_stopTime = Clock::now();
_diffDuration = _stopTime - _startTime;
return *this;
}
inline unsigned long ntests() const
{ return _ntests; }
inline TestType<>::Value type() const
{ return TestType<>::value(_type); }
Clock::duration diffDuration() const
{ return _diffDuration; }
template <typename V>
inline V deltaDurationCount() const
{ return std::chrono::duration<V>(_diffDuration).count(); }
template <typename V>
inline V averageDurationCount() const
{ return std::chrono::duration<V>(_diffDuration / _ntests).count(); }
inline bool operator<(const DispatchTest& rhs) const {
return (_stopTime - _startTime) < (rhs._stopTime - rhs._startTime);
}
private:
unsigned long _ntests;
ETestType _type;
TimePoint _startTime;
TimePoint _stopTime;
Clock::duration _diffDuration;
};
template<
template <typename TFT> typename F,
typename T = Object
>
static inline DispatchTest executeTest(size_t ntests, const T& subject) {
DispatchTest test(ntests);
auto facade = makeFacade<F>(subject);
test.test([&]() { facade->method(); }, DeductTestType<F>::id);
return test;
}
template <typename T = Object>
static inline DispatchTest executeSimpleTest(size_t ntests, const T& subject) {
DispatchTest test(ntests);
test.test([&]() { subject.method(); }, ETestType::Raw);
return test;
}
static inline std::ostream& showResults(const DispatchTest& test, std::ostream& os = std::cerr) {
auto testType = test.type();
std::stringstream ss;
ss << "* Test results for '" << testType.name << "': "
<< std::endl << "\tNumber of calls: " << test.ntests()
<< std::endl << "\tTotal time: " << test.deltaDurationCount<double>() << " second(s)"
<< std::endl << "\tAverage time: " << test.averageDurationCount<double>() << " second(s)"
<< std::endl;
return os << ss.str();
}
static inline std::ostream& exportResults(const DispatchTest& test, std::ostream& os = std::cout) {
auto testType = test.type();
std::stringstream ss;
ss
<< BUILD_TYPE
<< " " << (int)testType.id
<< " " << test.ntests()
<< " " << test.deltaDurationCount<double>()
<< " " << test.averageDurationCount<double>()
<< " " << test.diffDuration().count()
<< " " << DispatchTest::Clock::period::num
<< " " << DispatchTest::Clock::period::den
<< " # " << BUILD_STR << ", " << testType.name
<< std::endl;
return os << ss.str();
}
struct Options {
int ntests = NTESTS;
std::string executableName = "__noname__";
std::string outputFile = "-";
};
static void help(int argc, char** argv) {
}
int main(int argc, char** argv) {
unsigned long ntests = NTESTS;
int argIdx = 0;
char **arg = ++argv;
if (argc > 1) {
ntests = std::stoi(argv[1]);
}
std::cerr << "Starting tests for build '" BUILD_STR "'..." << std::endl;
Object subject;
std::vector<DispatchTest> tests = {
executeSimpleTest(ntests, subject),
executeTest<InheritanceInterface>(ntests, subject),
executeTest<PointerInterface>(ntests, subject),
executeTest<ClosureInterface>(ntests, subject)
};
std::cerr << "Finished tests. Results ordered by best time." << std::endl;
std::sort(tests.begin(), tests.end());
std::cerr << "Showing results..." << std::endl;
std::for_each(tests.begin(), tests.end(), [](DispatchTest test) {
showResults(test);
});
std::cerr << "Exporting results..." << std::endl;
std::for_each(tests.begin(), tests.end(), [](DispatchTest test) {
exportResults(test);
});
std::cerr << "Finished." << std::endl;
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment