Permalink
Browse files

Replace std::function with hyper_function for a 37% speedup in compil…

…e time.

Before:

    time c++ -O2 -DMAC -I/usr/local/include -std=c++11 -march=native
             -W -Wall -Wextra -Werror -pedantic -Wno-format-pedantic
             -Wno-missing-field-initializers -Wno-unused-parameter
             -DCAP_GLEW=0 -DCAP_PNG=0    -c hyper.cpp -o hyper.o

    real 2m22.508s
    user 2m20.625s
    sys  0m1.648s

After:

    time c++ -O2 -DMAC -I/usr/local/include -std=c++11 -march=native
             -W -Wall -Wextra -Werror -pedantic -Wno-format-pedantic
             -Wno-missing-field-initializers -Wno-unused-parameter
             -DCAP_GLEW=0 -DCAP_PNG=0    -c hyper.cpp -o hyper.o

    real 1m30.515s
    user 1m29.793s
    sys  0m0.689s

Comparing object file size:

    -rw-r--r--  1 ajo  staff  8215036 Jan  5 20:46 old-hyper.o
    -rw-r--r--  1 ajo  staff  7538072 Jan  5 20:47 new-hyper.o

Comparing number of symbols:

    nm old-hyper.o | wc -l  => 12590
    nm new-hyper.o | wc -l  =>  9742

No appreciable difference in link time; the linker takes less than
half a second in either case.
  • Loading branch information...
Quuxplusone committed Jan 6, 2019
1 parent f6f96e1 commit 4b3b2fd15c7cfc1be3202e290eff3b654d829fae
Showing with 72 additions and 1 deletion.
  1. +3 −1 hyper.h
  2. +69 −0 hyper_function.h
@@ -7,6 +7,7 @@
#define VERNUM_HEX 0xA504

#include <stdarg.h>
#include "hyper_function.h"

namespace hr {

@@ -15,6 +16,8 @@ void ignore(T&&) {
// placate GCC's overzealous -Wunused-result
}

template<class Sig> using function = hyper_function<Sig>;

// functions and types used from the standard library
using std::vector;
using std::map;
@@ -24,7 +27,6 @@ using std::sort;
using std::multimap;
using std::set;
using std::string;
using std::function;
using std::pair;
using std::tuple;
using std::shared_ptr;
@@ -0,0 +1,69 @@
#pragma once

#include <type_traits>

template<class Sig>
class hyper_function;

template<class R, class... Args>
struct hyper_function_state_base {
virtual R call(Args...) const = 0;
virtual hyper_function_state_base *clone() const = 0;
virtual ~hyper_function_state_base() = default;
};

template<class T, class R, class... Args>
struct hyper_function_state : hyper_function_state_base<R, Args...> {
using Self = hyper_function_state<T, R, Args...>;
T t_;
explicit hyper_function_state(T t) : t_(std::move(t)) {}
R call(Args... args) const override {
return const_cast<T&>(t_)(static_cast<Args&&>(args)...);
}
hyper_function_state_base<R, Args...> *clone() const override {
return new Self(*this);
}
};

template<class R, class... Args>
class hyper_function<R(Args...)>
{
hyper_function_state_base<R, Args...> *ptr_ = nullptr;
public:
hyper_function() = default;

template<class Callable, class = decltype(R(std::declval<typename std::decay<Callable>::type>()(std::declval<Args>()...)))>
hyper_function(Callable&& t) :
ptr_(new hyper_function_state<typename std::decay<Callable>::type, R, Args...>(static_cast<Callable&&>(t)))
{}

~hyper_function() {
delete ptr_;
}

hyper_function(hyper_function& rhs) : ptr_(rhs.ptr_ ? rhs.ptr_->clone() : nullptr) {}
hyper_function(const hyper_function& rhs) : ptr_(rhs.ptr_ ? rhs.ptr_->clone() : nullptr) {}
hyper_function(hyper_function&& rhs) noexcept : ptr_(rhs.ptr_) { rhs.ptr_ = nullptr; }
hyper_function(const hyper_function&& rhs) = delete;

void operator=(const hyper_function& rhs) {
if (&rhs != this) {
delete ptr_;
ptr_ = (rhs.ptr_ ? rhs.ptr_->clone() : nullptr);
}
}
void operator=(hyper_function&& rhs) noexcept {
if (&rhs != this) {
delete ptr_;
ptr_ = rhs.ptr_; rhs.ptr_ = nullptr;
}
}

R operator()(Args... args) const {
return ptr_->call(static_cast<Args&&>(args)...);
}

explicit operator bool() const noexcept {
return ptr_ != nullptr;
}
};

0 comments on commit 4b3b2fd

Please sign in to comment.