diff --git a/PMR/homework/CMakeLists.txt b/PMR/homework/CMakeLists.txt new file mode 100644 index 0000000..1a12b97 --- /dev/null +++ b/PMR/homework/CMakeLists.txt @@ -0,0 +1,35 @@ +cmake_minimum_required(VERSION 3.29) +project(pmr_homework_guoci) + +message("Building with CMake version: ${CMAKE_VERSION}") + +set(CMAKE_CXX_STANDARD 20) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + +add_executable(bm main.cpp) + +include(FetchContent) +FetchContent_Declare(googletest + GIT_REPOSITORY https://github.com/google/googletest.git + GIT_TAG main) +FetchContent_MakeAvailable(googletest) + +FetchContent_Declare(benchmark + GIT_REPOSITORY https://github.com/google/benchmark.git + GIT_TAG main) +FetchContent_MakeAvailable(benchmark) + +FetchContent_Declare(nlohmann_json + GIT_REPOSITORY https://github.com/nlohmann/json.git + GIT_TAG master) +FetchContent_MakeAvailable(nlohmann_json) + +find_package(benchmark REQUIRED) + +target_link_libraries(bm PRIVATE benchmark::benchmark nlohmann_json::nlohmann_json) + +target_compile_options(bm PRIVATE + -Wall -Wextra -Wshadow -Wconversion -pedantic -Wstrict-overflow + -Wnrvo + -Wuninitialized +) \ No newline at end of file diff --git a/PMR/homework/allocator.hpp b/PMR/homework/allocator.hpp new file mode 100644 index 0000000..36688eb --- /dev/null +++ b/PMR/homework/allocator.hpp @@ -0,0 +1,69 @@ +#pragma once + +#include +#include + +namespace gc { + template + inline thread_local typename Tag::memory_resource_type *mr = nullptr; + + template + class allocator { + public: + using value_type = T; + using propagate_on_container_move_assignment = std::true_type; + using is_always_equal = std::true_type; + + allocator() = default; + + template + constexpr explicit allocator(const allocator &) noexcept { + } + + [[nodiscard]] T *allocate(size_t n); + + void deallocate(T *p, size_t n); + }; + + template + [[nodiscard]] constexpr bool + operator==(allocator const & /*unused*/, allocator const & /*unused*/) noexcept { + return true; + } + + template + T *allocator::allocate(const size_t n) { + return static_cast(mr->allocate(n * sizeof(T), alignof(T))); + } + + template + void allocator::deallocate(T *p, const size_t n) { + mr->deallocate(p, n * sizeof(T), alignof(T)); + } + + + + template< + class Tag, + class CharT, + class Traits = std::char_traits > + using basic_string = + std::basic_string >; + + template + using string = basic_string; + + + struct monotonic_buffer_resource_devirt final : std::pmr::monotonic_buffer_resource { + // this class is needed to help the compiler to devirtualize the calls + using monotonic_buffer_resource::monotonic_buffer_resource; + + void *allocate(size_t bytes, size_t align) { + return monotonic_buffer_resource::do_allocate(bytes, align); + } + + void deallocate(void *p, std::size_t bytes, std::size_t alignment) { + monotonic_buffer_resource::do_deallocate(p, bytes, alignment); + }; + }; +} diff --git a/PMR/homework/main.cpp b/PMR/homework/main.cpp new file mode 100644 index 0000000..1f0f1f9 --- /dev/null +++ b/PMR/homework/main.cpp @@ -0,0 +1,113 @@ +#include "allocator.hpp" + +#include +#include +#include "nlohmann/json.hpp" + + + +std::string load_file(const std::string &path) { + // std::cerr << std::filesystem::current_path() << std::endl; + FILE *f = fopen(path.c_str(), "rb"); + fseek(f, 0, SEEK_END); + auto const size = ftell(f); + std::string s; + s.resize(static_cast(size)); + fseek(f, 0, SEEK_SET); + auto const nread = fread(&s[0], 1, static_cast(size), f); + s.resize(nread); + fclose(f); + return s; +} + +/* +// Your homework: get this compiling and working so that PMR permeates the json parser when desired +static void nlohmann_JSON_PMR(benchmark::State &state) +{ + using pmr_json = nlohmann::basic_json; + auto s = load_file("citm_catalog.json"); + for (auto _ : state) { + auto jv = pmr_json::parse(s.begin(), s.end()); + } +} +BENCHMARK(nlohmann_JSON_PMR); +*/ + + +struct monotonic { + using memory_resource_type = std::pmr::monotonic_buffer_resource; +}; + +template +using allocator_monotonic = gc::allocator; + +static void nlohmann_JSON_MBR_stateless_alloc(benchmark::State &state, std::string_view s) { + constexpr size_t bufsize = 1 << 25; + thread_local auto buf = std::make_unique_for_overwrite(bufsize); + thread_local std::pmr::monotonic_buffer_resource mbr{buf.get(), bufsize, std::pmr::null_memory_resource()}; + + using pmr_json = nlohmann::basic_json, bool, std::int64_t, + std::uint64_t, + double, + allocator_monotonic>; + gc::mr = &mbr; + for (auto _: state) { + mbr.release(); + auto jv = pmr_json::parse(s.begin(), s.end()); + benchmark::DoNotOptimize(jv); + } +} + +struct monotonic_devirt { + using memory_resource_type = gc::monotonic_buffer_resource_devirt; +}; + +template +using allocator_monotonic_devirt = gc::allocator; + +static void nlohmann_JSON_MBR_stateless_alloc_devirt(benchmark::State &state, std::string_view s) { + using pmr_json = nlohmann::basic_json, bool, std::int64_t, + std::uint64_t, double, + allocator_monotonic_devirt>; + constexpr size_t bufsize = 1 << 25; + thread_local auto buf = std::make_unique_for_overwrite(bufsize); + + thread_local gc::monotonic_buffer_resource_devirt mbr{buf.get(), bufsize, std::pmr::null_memory_resource()}; + gc::mr = &mbr; + for (auto _: state) { + mbr.release(); + auto jv = pmr_json::parse(s.begin(), s.end()); + benchmark::DoNotOptimize(jv); + } +} + +static void nlohmann_JSON_Default(benchmark::State &state, std::string_view s) { + for (auto _: state) { + auto jv = nlohmann::json::parse(s.begin(), s.end()); + benchmark::DoNotOptimize(jv); + } +} + +static void JSON_Perf(benchmark::State &state, void (*test)(benchmark::State &, const std::string_view), + const std::string &filename) { + auto s = load_file(filename); + test(state, s); + state.SetBytesProcessed(static_cast(state.iterations() * s.size())); +} + +#define ADD_BENCHMARK(func, filename) BENCHMARK_CAPTURE(JSON_Perf, func-filename, func, filename) + +ADD_BENCHMARK(nlohmann_JSON_Default, "citm_catalog.json"); +ADD_BENCHMARK(nlohmann_JSON_MBR_stateless_alloc, "citm_catalog.json"); +ADD_BENCHMARK(nlohmann_JSON_MBR_stateless_alloc_devirt, "citm_catalog.json"); + +ADD_BENCHMARK(nlohmann_JSON_Default, "gsoc-2018.json"); +ADD_BENCHMARK(nlohmann_JSON_MBR_stateless_alloc, "gsoc-2018.json"); +ADD_BENCHMARK(nlohmann_JSON_MBR_stateless_alloc_devirt, "gsoc-2018.json"); + +ADD_BENCHMARK(nlohmann_JSON_Default, "github_events.json"); +ADD_BENCHMARK(nlohmann_JSON_MBR_stateless_alloc, "github_events.json"); +ADD_BENCHMARK(nlohmann_JSON_MBR_stateless_alloc_devirt, "github_events.json"); + + +BENCHMARK_MAIN();