#include #include #ifdef _WIN32 #include #include #else #include #endif #include #include #include #include #include #include #include #include #include #include #include #include #include "../Renderive/architecture/Renderable.h" namespace { class Counting_Resource final : public std::pmr::memory_resource { public: std::uint64_t allocation_count{}; std::uint64_t deallocation_count{}; std::uint64_t current_bytes{}; std::uint64_t peak_bytes{}; private: void* do_allocate(std::size_t size, std::size_t alignment) override { void* result = std::pmr::new_delete_resource()->allocate(size, alignment); ++allocation_count; current_bytes += size; peak_bytes = std::max(peak_bytes, current_bytes); return result; } void do_deallocate(void* address, std::size_t size, std::size_t alignment) override { std::pmr::new_delete_resource()->deallocate(address, size, alignment); ++deallocation_count; current_bytes -= size; } bool do_is_equal(const std::pmr::memory_resource& other) const noexcept override { return this == &other; } }; struct Benchmark_Waterfall_Row { int tick{}; std::vector frequency; }; struct Benchmark_Time_Tick { QTime time; int tick{}; }; struct Benchmark_Result { double renderable_ms{}; double waterfall_ms{}; double time_audio_ms{}; }; volatile std::uint64_t benchmark_sink{}; template std::shared_ptr make_two_allocation_shared(std::pmr::memory_resource* resource) { std::pmr::polymorphic_allocator object_allocator(resource); T* object = object_allocator.allocate(1); try { std::allocator_traits::construct(object_allocator, object); } catch (...) { object_allocator.deallocate(object, 1); throw; } auto deleter = [object_allocator](T* value) mutable { std::allocator_traits::destroy(object_allocator, value); object_allocator.deallocate(value, 1); }; return std::shared_ptr(object, std::move(deleter), std::pmr::polymorphic_allocator(resource)); } template double measure_milliseconds(Handler handler) { auto begin = std::chrono::steady_clock::now(); handler(); auto end = std::chrono::steady_clock::now(); return std::chrono::duration(end - begin).count(); } double benchmark_renderables(std::pmr::memory_resource* resource, bool two_allocations) { return measure_milliseconds([resource, two_allocations]() { std::pmr::vector> owners(resource); owners.reserve(20000); for (int i = 0; i < 20000; ++i) { if (two_allocations) owners.push_back(make_two_allocation_shared(resource)); else owners.push_back(std::allocate_shared(std::pmr::polymorphic_allocator(resource))); } benchmark_sink += owners.size(); owners.clear(); }); } double benchmark_waterfall(std::pmr::memory_resource* resource) { static constexpr std::array frequency_sizes{64, 512, 8192, 8193, 128}; static constexpr std::array time_sizes{32, 96, 48, 64, 24}; return measure_milliseconds([resource]() { for (int round = 0; round < 32; ++round) { std::pmr::vector write_buffer(resource); std::pmr::vector record_buffer(resource); std::pmr::vector snapshot_buffer(resource); std::pmr::vector row_buffer(resource); std::pmr::vector pending_rows(resource); for (std::size_t stage = 0; stage < frequency_sizes.size(); ++stage) { std::size_t row_bytes = static_cast(frequency_sizes[stage]) * sizeof(double); std::size_t record_bytes = row_bytes + sizeof(int); write_buffer.resize(record_bytes); record_buffer.resize(record_bytes); snapshot_buffer.resize(record_bytes * static_cast(time_sizes[stage])); row_buffer.resize(frequency_sizes[stage]); pending_rows.clear(); for (int row = 0; row < 8; ++row) { Benchmark_Waterfall_Row value; value.tick = row; value.frequency.resize(frequency_sizes[stage]); pending_rows.push_back(std::move(value)); } write_buffer.front() = static_cast(round); row_buffer.front() = static_cast(round); } benchmark_sink += write_buffer.size() + snapshot_buffer.size() + pending_rows.size(); } }); } double benchmark_time_audio(std::pmr::memory_resource* resource) { static constexpr std::array point_sizes{32, 256, 4096, 64}; return measure_milliseconds([resource]() { for (int round = 0; round < 64; ++round) { std::pmr::vector time_snapshot(resource); std::pmr::vector power_values(resource); std::pmr::list time_ticks(resource); for (int size : point_sizes) { time_snapshot.resize(size); power_values.resize(size); for (int tick = 0; tick < size; ++tick) time_ticks.push_front({QTime::fromMSecsSinceStartOfDay(tick), tick}); while (time_ticks.size() > static_cast(size)) time_ticks.pop_back(); if (!power_values.empty()) power_values.back() = static_cast(round); } benchmark_sink += time_snapshot.size() + power_values.size() + time_ticks.size(); } }); } std::uint64_t peak_working_set_bytes() { #ifdef _WIN32 PROCESS_MEMORY_COUNTERS_EX counters{}; counters.cb = sizeof(counters); GetProcessMemoryInfo(GetCurrentProcess(), reinterpret_cast(&counters), sizeof(counters)); return counters.PeakWorkingSetSize; #else rusage usage{}; getrusage(RUSAGE_SELF, &usage); return static_cast(usage.ru_maxrss) * 1024; #endif } Benchmark_Result run_benchmark(std::pmr::memory_resource* resource, bool two_allocations) { Benchmark_Result result; result.renderable_ms = benchmark_renderables(resource, two_allocations); result.waterfall_ms = benchmark_waterfall(resource); result.time_audio_ms = benchmark_time_audio(resource); return result; } } int main(int argc, char** argv) { if (argc != 2) return 1; std::string name = argv[1]; Counting_Resource upstream; std::unique_ptr pool; std::pmr::memory_resource* resource = &upstream; bool two_allocations = false; if (name == "old") { two_allocations = true; } else if (name == "default") { pool = std::make_unique(&upstream); resource = pool.get(); } else { std::pmr::pool_options options; if (name == "64x64k") { options.max_blocks_per_chunk = 64; options.largest_required_pool_block = 64 * 1024; } else if (name == "32x32k") { options.max_blocks_per_chunk = 32; options.largest_required_pool_block = 32 * 1024; } else if (name == "64x128k") { options.max_blocks_per_chunk = 64; options.largest_required_pool_block = 128 * 1024; } else { return 2; } pool = std::make_unique(options, &upstream); resource = pool.get(); } Benchmark_Result result = run_benchmark(resource, two_allocations); std::cout << name << ',' << result.renderable_ms << ',' << result.waterfall_ms << ',' << result.time_audio_ms << ',' << upstream.allocation_count << ',' << upstream.current_bytes << ',' << upstream.peak_bytes << ',' << peak_working_set_bytes() << '\n'; return 0; }