Skip to content

Instantly share code, notes, and snippets.

@isidorostsa
Last active October 8, 2023 16:34
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save isidorostsa/7426389295f329fa0feaabb55639d6bb to your computer and use it in GitHub Desktop.
Save isidorostsa/7426389295f329fa0feaabb55639d6bb to your computer and use it in GitHub Desktop.
The C++ code used to benchmark the performance gains of relocating containers "trivially"
#define ANKERL_NANOBENCH_IMPLEMENT
#include "nanobench.h"
#include <hpx/init.hpp>
#include <hpx/parallel/algorithms/uninitialized_relocate.hpp>
#include <fstream>
#include <iostream>
#include <forward_list>
#include <memory>
#include <random>
#include <vector>
using hpx::experimental::uninitialized_relocate_n;
std::tuple containers =
std::make_tuple(std::vector<int>{}, std::forward_list<int>{},
std::unique_ptr<int>{}, std::shared_ptr<int>{});
ankerl::nanobench::Bench bench;
using TR = int;
using NTR = uint;
HPX_DECLARE_TRIVIALLY_RELOCATABLE(std::vector<TR>);
HPX_DECLARE_TRIVIALLY_RELOCATABLE(std::forward_list<TR>);
HPX_DECLARE_TRIVIALLY_RELOCATABLE(std::unique_ptr<TR>);
HPX_DECLARE_TRIVIALLY_RELOCATABLE(std::shared_ptr<TR>);
template <typename T, typename TR_NTR, typename Ex>
std::vector<std::string> get_strings(size_t size) {
std::vector<std::string> strings;
if constexpr (std::is_same_v<T, std::vector<TR_NTR>>) {
strings.push_back("std::vector");
} else if constexpr (std::is_same_v<T, std::forward_list<TR_NTR>>) {
strings.push_back("std::forward_list");
} else if constexpr (std::is_same_v<T, std::unique_ptr<TR_NTR>>) {
strings.push_back("std::unique_ptr");
} else if constexpr (std::is_same_v<T, std::shared_ptr<TR_NTR>>) {
strings.push_back("std::shared_ptr");
}
if constexpr (std::is_same_v<TR_NTR, TR>) {
strings.push_back("trivial");
} else if constexpr (std::is_same_v<TR_NTR, NTR>) {
strings.push_back("non-trivial");
}
if constexpr (std::is_same_v<Ex, hpx::execution::sequenced_policy>) {
strings.push_back("sequenced");
} else if constexpr (std::is_same_v<Ex, hpx::execution::parallel_policy>) {
strings.push_back("parallel");
}
strings.push_back(std::to_string(size));
return strings;
}
template <typename Cont, typename TR_NTR>
struct is_un_or_sh_ptr
: std::bool_constant<std::is_same_v<Cont, std::unique_ptr<TR_NTR>> ||
std::is_same_v<Cont, std::shared_ptr<TR_NTR>>> {};
template <typename T, typename TR_NTR> void fill_up(T *ptr, size_t size) {
ankerl::nanobench::Rng rng;
if constexpr (is_un_or_sh_ptr<T, TR_NTR>::value) {
for (size_t i = 0; i < size; ++i) {
new (ptr + i) T{new TR_NTR{rng() % 128}};
}
} else {
for (size_t i = 0; i < size; ++i) {
new (ptr + i) T{rng() % 128, 2, 3, 4, 5};
}
}
}
template <typename T, typename TR_NTR, typename Ex> auto test(int size) {
std::byte *buffer1 = static_cast<std::byte *>(std::malloc(size * sizeof(T)));
std::byte *buffer2 = static_cast<std::byte *>(std::malloc(size * sizeof(T)));
T *v_ptr = reinterpret_cast<T *>(buffer1);
fill_up<T, TR_NTR>(v_ptr, size);
T *v_ptr2 = reinterpret_cast<T *>(buffer2);
ankerl::nanobench::Config cfg;
auto strings = get_strings<T, TR_NTR, Ex>(size);
std::string name = strings[1] + " relocation of " + strings[0];
std::string fname = "./results/" + strings[1] + "_" + strings[0] + ".json";
std::fstream file(fname, std::ios::out);
ankerl::nanobench::Bench()
.config(cfg)
.context("container", strings[0])
.context("triviality", strings[1])
.context("execution", strings[2])
.context("size", strings[3])
.name(name)
.run([&] {
auto a = uninitialized_relocate_n(Ex{}, v_ptr, size, v_ptr2);
ankerl::nanobench::doNotOptimizeAway(a);
auto b = uninitialized_relocate_n(Ex{}, v_ptr2, size, v_ptr);
ankerl::nanobench::doNotOptimizeAway(b);
})
.render(ankerl::nanobench::templates::pyperf(), file);
std::free(buffer1);
std::free(buffer2);
}
#define TEST(Cont, TR_NTR, Ex, size) test<Cont<TR_NTR>, TR_NTR, Ex>(size)
#define TEST_OVER_CONT(TR_NTR, Ex, size) \
TEST(std::vector, TR_NTR, Ex, size); \
TEST(std::forward_list, TR_NTR, Ex, size); \
TEST(std::unique_ptr, TR_NTR, Ex, size); \
TEST(std::shared_ptr, TR_NTR, Ex, size);
#define TEST_OVER_CONT_TRIVIALITY(Ex, size) \
TEST_OVER_CONT(TR, Ex, size); \
TEST_OVER_CONT(NTR, Ex, size);
#define TEST_OVER_CONT_TRIVIALITY_EXECUTION(size) \
TEST_OVER_CONT_TRIVIALITY(hpx::execution::sequenced_policy, size);
// TEST_OVER_CONT_TRIVIALITY(hpx::execution::parallel_policy, size);
int hpx_main(int argc, char **argv) {
size_t size = atoi(argv[1]);
TEST_OVER_CONT_TRIVIALITY_EXECUTION(size)
return hpx::finalize();
}
int main(int argc, char *argv[]) {
return hpx::local::init(hpx_main, argc, argv);
}
size_of_buffer_in_objects type trivial_relocation_speedup
100 unique_ptr 8.3
100 shared_ptr 3.33
100 forward_list 2.97
100 vector 6.79
1000 unique_ptr 15.96
1000 shared_ptr 5.75
1000 forward_list 4.83
1000 vector 6.34
10000 unique_ptr 9.52
10000 shared_ptr 6.35
10000 forward_list 5.18
10000 vector 6.47
100000 unique_ptr 4.28
100000 shared_ptr 0.94
100000 forward_list 2.16
100000 vector 1.16
1000000 unique_ptr 1.59
1000000 shared_ptr 1.24
1000000 forward_list 0.86
1000000 vector 1.31
10000000 unique_ptr 1.44
10000000 shared_ptr 1.15
10000000 vector 1.21
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment