Skip to content

Instantly share code, notes, and snippets.

@Vezhur
Created October 21, 2022 07:59
Show Gist options
  • Save Vezhur/a29596a8387f513aad0c5b35b14c758e to your computer and use it in GitHub Desktop.
Save Vezhur/a29596a8387f513aad0c5b35b14c758e to your computer and use it in GitHub Desktop.
#include <memory>
#include <vector>
#include <random>
struct Stmt_RTTI { virtual ~Stmt_RTTI() = default; };
struct IfStmt_RTTI : Stmt_RTTI { };
struct ForStmt_RTTI : Stmt_RTTI { };
struct Stmt_WithEnum
{
enum Type { IfStmt, ForStmt, DoStmt, WhileStmt };
Stmt_WithEnum(Type kind) : m_kind { kind } { }
virtual ~Stmt_WithEnum() = default;
Type Kind() const { return m_kind; }
private:
const Type m_kind;
};
namespace Solution1
{
template <Stmt_WithEnum::Type K>
struct type_from_kind;
template <typename T>
struct kind_from_type;
#define MAKE_STMT_TRAITS(t, k) \
template <> struct Solution1::type_from_kind<k> \
{ \
using type = t; \
}; \
template <> struct Solution1::kind_from_type<t> \
{ \
static constexpr auto value = k; \
};
template <typename T, typename Kind, typename ...Kinds>
bool IsA(T stmt, Kind kind, Kinds ...kinds) noexcept
{
return stmt &&
((stmt->Kind() == kind) || ... || (stmt->Kind() == kinds));
}
template <typename To, typename From>
requires std::is_pointer_v<To>
&& requires { static_cast<To>(std::declval<From *>()); }
auto dyn_cast(From *p) noexcept
{
using ResultType = std::remove_cvref_t<
std::remove_pointer_t< std::remove_cvref_t<To> >
>;
return IsA(p, kind_from_type<ResultType>::value)
? static_cast<To>(p)
: nullptr;
}
}
struct IfStmt_WithEnum : Stmt_WithEnum
{
IfStmt_WithEnum() : Stmt_WithEnum { Stmt_WithEnum::IfStmt } {}
static bool classof(const Stmt_WithEnum *p) noexcept
{
return p && p->Kind() == Stmt_WithEnum::IfStmt;
}
};
MAKE_STMT_TRAITS(IfStmt_WithEnum, Stmt_WithEnum::IfStmt)
struct ForStmt_WithEnum : Stmt_WithEnum
{
ForStmt_WithEnum() : Stmt_WithEnum { Stmt_WithEnum::ForStmt } {}
static bool classof(const Stmt_WithEnum *p) noexcept
{
return p && p->Kind() == Stmt_WithEnum::ForStmt;
}
};
MAKE_STMT_TRAITS(ForStmt_WithEnum, Stmt_WithEnum::ForStmt)
namespace Solution2
{
template <typename To, typename From>
bool IsA(From *p) noexcept
{
using ResultType = std::remove_cvref_t<
std::remove_pointer_t< std::remove_cvref_t<To> >
>;
return ResultType::classof(p);
}
template <typename To, typename From>
requires std::is_pointer_v<To>
&& requires { static_cast<To>(std::declval<From *>()); }
auto dyn_cast(From *p) noexcept
{
using ResultType = std::remove_cvref_t<
std::remove_pointer_t< std::remove_cvref_t<To> >
>;
return IsA<ResultType>(p) ? static_cast<To>(p) : nullptr;
}
}
std::unique_ptr<Stmt_RTTI> factory_1()
{
static std::mt19937_64 Generator { 0 };
std::uniform_int_distribution d { 0, 1 };
switch (d(Generator))
{
case 0:
return std::make_unique<IfStmt_RTTI>();
case 1:
return std::make_unique<ForStmt_RTTI>();
}
std::terminate();
}
std::unique_ptr<Stmt_WithEnum> factory_2()
{
static std::mt19937_64 Generator { 0 };
std::uniform_int_distribution d { 0, 1 };
switch (d(Generator))
{
case 0:
return std::make_unique<IfStmt_WithEnum>();
case 1:
return std::make_unique<ForStmt_WithEnum>();
}
std::terminate();
}
static void StmtRTTI_Benchmark(benchmark::State& state) {
std::vector<std::unique_ptr<Stmt_RTTI>> vec;
const auto size = 1'000'000u;
vec.reserve(size);
for (size_t i = 0; i < size; ++i)
{
vec.push_back(factory_1());
}
// Code inside this loop is measured repeatedly
for (auto _ : state)
{
for (const auto &stmt : vec)
{
if (auto ifStmt = dynamic_cast<const IfStmt_RTTI *>(stmt.get()))
{
// Make sure the variable is not optimized away by compiler
benchmark::DoNotOptimize(ifStmt);
}
else if (auto forStmt = dynamic_cast<const ForStmt_RTTI *>(stmt.get()))
{
// Make sure the variable is not optimized away by compiler
benchmark::DoNotOptimize(forStmt);
}
}
}
}
// Register the function as a benchmark
BENCHMARK(StmtRTTI_Benchmark);
static void StmtWithEnum_Benchmark_1(benchmark::State& state) {
std::vector<std::unique_ptr<Stmt_WithEnum>> vec;
const auto size = 1'000'000u;
vec.reserve(size);
for (size_t i = 0; i < size; ++i)
{
vec.push_back(factory_2());
}
// Code inside this loop is measured repeatedly
for (auto _ : state)
{
for (const auto &stmt : vec)
{
if (auto ifStmt =
Solution1::dyn_cast<const IfStmt_WithEnum *>(stmt.get()))
{
// Make sure the variable is not optimized away by compiler
benchmark::DoNotOptimize(ifStmt);
}
else if (auto forStmt =
Solution1::dyn_cast<const ForStmt_WithEnum *>(stmt.get()))
{
// Make sure the variable is not optimized away by compiler
benchmark::DoNotOptimize(forStmt);
}
}
}
}
BENCHMARK(StmtWithEnum_Benchmark_1);
static void StmtWithEnum_Benchmark_2(benchmark::State& state)
{
std::vector<std::unique_ptr<Stmt_WithEnum>> vec;
const auto size = 1'000'000u;
vec.reserve(size);
for (size_t i = 0; i < size; ++i)
{
vec.push_back(factory_2());
}
// Code inside this loop is measured repeatedly
for (auto _ : state)
{
for (const auto &stmt : vec)
{
if (auto ifStmt = Solution2::dyn_cast<
const IfStmt_WithEnum *>(stmt.get()))
{
// Make sure the variable is not optimized away by compiler
benchmark::DoNotOptimize(ifStmt);
}
else if (auto forStmt =
Solution2::dyn_cast<const ForStmt_WithEnum *>(stmt.get()))
{
// Make sure the variable is not optimized away by compiler
benchmark::DoNotOptimize(forStmt);
}
}
}
}
BENCHMARK(StmtWithEnum_Benchmark_2);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment