Skip to content

Commit

Permalink
Merge pull request #1987 from STEllAR-GROUP/function_small_object_opt…
Browse files Browse the repository at this point in the history
…imization

Make configurable size of internal storage in util::function
  • Loading branch information
hkaiser committed Feb 16, 2016
2 parents 8aac541 + 77f9662 commit f613c8e
Show file tree
Hide file tree
Showing 7 changed files with 148 additions and 30 deletions.
34 changes: 18 additions & 16 deletions hpx/util/detail/basic_function.hpp
Expand Up @@ -24,6 +24,7 @@
#include <type_traits>
#include <typeinfo>
#include <utility>
#include <cstring>

namespace hpx { namespace util { namespace detail
{
Expand Down Expand Up @@ -126,22 +127,23 @@ namespace hpx { namespace util { namespace detail
public:
function_base() HPX_NOEXCEPT
: vptr(&empty_table)
, object(0)
{
vtable::default_construct<empty_function<R(Ts...)> >(&object);
std::memset(object, 0, vtable::function_storage_size);
vtable::default_construct<empty_function<R(Ts...)> >(object);
}

function_base(function_base&& other) HPX_NOEXCEPT
: vptr(other.vptr)
, object(other.object) // move-construct
{
// move-construct
std::memcpy(object, other.object, vtable::function_storage_size);
other.vptr = &empty_table;
vtable::default_construct<empty_function<R(Ts...)> >(&other.object);
vtable::default_construct<empty_function<R(Ts...)> >(other.object);
}

~function_base()
{
vptr->delete_(&object);
vptr->delete_(object);
}

function_base& operator=(function_base&& other) HPX_NOEXCEPT
Expand All @@ -164,13 +166,13 @@ namespace hpx { namespace util { namespace detail
VTablePtr const* f_vptr = get_table_ptr<target_type>();
if (vptr == f_vptr)
{
vtable::reconstruct<target_type>(&object, std::forward<F>(f));
vtable::reconstruct<target_type>(object, std::forward<F>(f));
} else {
reset();
vtable::delete_<empty_function<R(Ts...)> >(&object);
vtable::delete_<empty_function<R(Ts...)> >(object);

vptr = f_vptr;
vtable::construct<target_type>(&object, std::forward<F>(f));
vtable::construct<target_type>(object, std::forward<F>(f));
}
} else {
reset();
Expand All @@ -181,10 +183,10 @@ namespace hpx { namespace util { namespace detail
{
if (!vptr->empty)
{
vptr->delete_(&object);
vptr->delete_(object);

vptr = &empty_table;
vtable::default_construct<empty_function<R(Ts...)> >(&object);
vtable::default_construct<empty_function<R(Ts...)> >(object);
}
}

Expand Down Expand Up @@ -230,7 +232,7 @@ namespace hpx { namespace util { namespace detail
if (vptr != f_vptr || empty())
return 0;

return &vtable::get<target_type>(&object);
return &vtable::get<target_type>(object);
}

template <typename T>
Expand All @@ -246,12 +248,12 @@ namespace hpx { namespace util { namespace detail
if (vptr != f_vptr || empty())
return 0;

return &vtable::get<target_type>(&object);
return &vtable::get<target_type>(object);
}

HPX_FORCEINLINE R operator()(Ts... vs) const
{
return vptr->invoke(&object, std::forward<Ts>(vs)...);
return vptr->invoke(object, std::forward<Ts>(vs)...);
}

private:
Expand All @@ -263,7 +265,7 @@ namespace hpx { namespace util { namespace detail

protected:
VTablePtr const *vptr;
mutable void *object;
mutable void* object[vtable::function_storage_size];
};

template <typename VTablePtr, typename R, typename ...Ts>
Expand Down Expand Up @@ -324,7 +326,7 @@ namespace hpx { namespace util { namespace detail
ar >> name;

this->vptr = detail::get_table_ptr<vtable_ptr>(name);
this->vptr->load_object(&this->object, ar, version);
this->vptr->load_object(this->object, ar, version);
}
}

Expand All @@ -337,7 +339,7 @@ namespace hpx { namespace util { namespace detail
std::string function_name = this->vptr->name;
ar << function_name;

this->vptr->save_object(&this->object, ar, version);
this->vptr->save_object(this->object, ar, version);
}
}

Expand Down
8 changes: 4 additions & 4 deletions hpx/util/detail/function_template.hpp
Expand Up @@ -83,12 +83,12 @@ namespace hpx { namespace util
{
detail::vtable::delete_<
detail::empty_function<R(Ts...)>
>(&this->object);
>(this->object);

this->vptr = other.vptr;
if (!this->vptr->empty)
{
this->vptr->copy(&this->object, &other.object);
this->vptr->copy(this->object, other.object);
}
}

Expand Down Expand Up @@ -117,12 +117,12 @@ namespace hpx { namespace util
reset();
detail::vtable::delete_<
detail::empty_function<R(Ts...)>
>(&this->object);
>(this->object);

this->vptr = other.vptr;
if (!this->vptr->empty)
{
this->vptr->copy(&this->object, &other.object);
this->vptr->copy(this->object, other.object);
}
}
return *this;
Expand Down
2 changes: 1 addition & 1 deletion hpx/util/detail/vtable/copyable_vtable.hpp
Expand Up @@ -18,7 +18,7 @@ namespace hpx { namespace util { namespace detail
template <typename T>
HPX_FORCEINLINE static void copy(void** v, void* const* src)
{
if (sizeof(T) <= sizeof(void*))
if (sizeof(T) <= vtable::function_storage_size)
{
new (v) T(get<T>(src));
} else {
Expand Down
13 changes: 8 additions & 5 deletions hpx/util/detail/vtable/vtable.hpp
Expand Up @@ -16,8 +16,11 @@

namespace hpx { namespace util { namespace detail
{
///////////////////////////////////////////////////////////////////////////
struct vtable
{
static const std::size_t function_storage_size = 3*sizeof(void*);

template <typename T>
HPX_FORCEINLINE static std::type_info const& get_type()
{
Expand All @@ -28,7 +31,7 @@ namespace hpx { namespace util { namespace detail
template <typename T>
HPX_FORCEINLINE static T& get(void** v)
{
if (sizeof(T) <= sizeof(void*))
if (sizeof(T) <= function_storage_size)
{
return *reinterpret_cast<T*>(v);
} else {
Expand All @@ -39,7 +42,7 @@ namespace hpx { namespace util { namespace detail
template <typename T>
HPX_FORCEINLINE static T const& get(void* const* v)
{
if (sizeof(T) <= sizeof(void*))
if (sizeof(T) <= function_storage_size)
{
return *reinterpret_cast<T const*>(v);
} else {
Expand All @@ -50,7 +53,7 @@ namespace hpx { namespace util { namespace detail
template <typename T>
HPX_FORCEINLINE static void default_construct(void** v)
{
if (sizeof(T) <= sizeof(void*))
if (sizeof(T) <= function_storage_size)
{
::new (static_cast<void*>(v)) T;
} else {
Expand All @@ -61,7 +64,7 @@ namespace hpx { namespace util { namespace detail
template <typename T, typename Arg>
HPX_FORCEINLINE static void construct(void** v, Arg&& arg)
{
if (sizeof(T) <= sizeof(void*))
if (sizeof(T) <= function_storage_size)
{
::new (static_cast<void*>(v)) T(std::forward<Arg>(arg));
} else {
Expand All @@ -86,7 +89,7 @@ namespace hpx { namespace util { namespace detail
template <typename T>
HPX_FORCEINLINE static void delete_(void** v)
{
if (sizeof(T) <= sizeof(void*))
if (sizeof(T) <= function_storage_size)
{
destruct<T>(v);
} else {
Expand Down
2 changes: 2 additions & 0 deletions tests/performance/local/CMakeLists.txt
Expand Up @@ -24,6 +24,7 @@ set(benchmarks
hpx_homogeneous_timed_task_spawn_executors
hpx_heterogeneous_timed_task_spawn
print_heterogeneous_payloads
skynet
timed_task_spawn
)

Expand Down Expand Up @@ -83,6 +84,7 @@ endif()

set(hpx_homogeneous_timed_task_spawn_executors_FLAGS DEPENDENCIES iostreams_component)
set(hpx_heterogeneous_timed_task_spawn_FLAGS DEPENDENCIES iostreams_component)
set(skynet_FLAGS DEPENDENCIES iostreams_component)

set(delay_baseline_FLAGS NOLIBS
DEPENDENCIES ${boost_library_dependencies})
Expand Down
111 changes: 111 additions & 0 deletions tests/performance/local/skynet.cpp
@@ -0,0 +1,111 @@
// Copyright (c) 2016 Hartmut Kaiser
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)

// This benchmark provides an equivalent for the benchmarks published at
// https://github.com/atemerev/skynet. It is called the Skynet 1M concurrency
// micro benchmark.
//
// It creates an actor (goroutine, whatever), which spawns 10 new actors, each
// of them spawns 10 more actors, etc. until one million actors are created on
// the final level. Then, each of them returns back its ordinal number (from 0
// to 999999), which are summed on the previous level and sent back upstream,
// until reaching the root actor. (The answer should be 499999500000).

// This code implements two versions of the skynet micro benchmark: a 'normal'
// and a futurized one.

#include <hpx/hpx_main.hpp>
#include <hpx/hpx.hpp>
#include <hpx/include/iostreams.hpp>

#include <vector>

///////////////////////////////////////////////////////////////////////////////
boost::int64_t skynet(boost::int64_t num, boost::int64_t size, boost::int64_t div)
{
if (size != 1)
{
size /= div;

std::vector<hpx::future<boost::int64_t> > results;
results.reserve(div);

for (boost::int64_t i = 0; i != div; ++i)
{
boost::int64_t sub_num = num + i * size;
results.push_back(hpx::async(skynet, sub_num, size, div));
}

hpx::wait_all(results);

boost::int64_t sum = 0;
for (auto & f : results)
sum += f.get();
return sum;
}
return num;
}

///////////////////////////////////////////////////////////////////////////////
hpx::future<boost::int64_t>
skynet_f(boost::int64_t num, boost::int64_t size, boost::int64_t div)
{
if (size != 1)
{
size /= div;

std::vector<hpx::future<boost::int64_t> > results;
results.reserve(div);

for (boost::int64_t i = 0; i != div; ++i)
{
boost::int64_t sub_num = num + i * size;
results.push_back(hpx::async(skynet_f, sub_num, size, div));
}

return hpx::dataflow(
[](std::vector<hpx::future<boost::int64_t> > && sums)
{
boost::int64_t sum = 0;
for (auto & f : sums)
sum += f.get();
return sum;
},
results);
}
return hpx::make_ready_future(num);
}

///////////////////////////////////////////////////////////////////////////////
int main()
{
{
boost::uint64_t t = hpx::util::high_resolution_clock::now();

hpx::future<boost::int64_t> result = hpx::async(skynet, 0, 1000000, 10);
result.wait();

t = hpx::util::high_resolution_clock::now() - t;

hpx::cout
<< "Result 1: " << result.get() << " in "
<< (t / 1e6) << " ms.\n";
}

{
boost::uint64_t t = hpx::util::high_resolution_clock::now();

hpx::future<boost::int64_t> result = hpx::async(skynet_f, 0, 1000000, 10);
result.wait();

t = hpx::util::high_resolution_clock::now() - t;

hpx::cout
<< "Result 2: " << result.get() << " in "
<< (t / 1e6) << " ms.\n";
}
return 0;
}

8 changes: 4 additions & 4 deletions tests/unit/util/function.cpp
Expand Up @@ -173,7 +173,7 @@ int hpx_main(variables_map& vm)
{
{
{
if (sizeof(small_object) <= sizeof(void*))
if (sizeof(small_object) <= hpx::util::detail::vtable::function_storage_size)
std::cout << "object is small\n";
else
std::cout << "object is large\n";
Expand All @@ -194,7 +194,7 @@ int hpx_main(variables_map& vm)
}

{
if (sizeof(big_object) <= sizeof(void*))
if (sizeof(big_object) <= hpx::util::detail::vtable::function_storage_size)
std::cout << "object is small\n";
else
std::cout << "object is large\n";
Expand All @@ -219,7 +219,7 @@ int hpx_main(variables_map& vm)
// non serializable version
{
{
if (sizeof(small_object) <= sizeof(void*))
if (sizeof(small_object) <= hpx::util::detail::vtable::function_storage_size)
std::cout << "object is small\n";
else
std::cout << "object is large\n";
Expand All @@ -240,7 +240,7 @@ int hpx_main(variables_map& vm)
}

{
if (sizeof(big_object) <= sizeof(void*))
if (sizeof(big_object) <= hpx::util::detail::vtable::function_storage_size)
std::cout << "object is small\n";
else
std::cout << "object is large\n";
Expand Down

0 comments on commit f613c8e

Please sign in to comment.