补充提交
This commit is contained in:
@@ -0,0 +1,208 @@
|
||||
#include <QTime>
|
||||
#include <vector>
|
||||
#ifdef _WIN32
|
||||
#include <Windows.h>
|
||||
#include <Psapi.h>
|
||||
#else
|
||||
#include <sys/resource.h>
|
||||
#endif
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <chrono>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <iostream>
|
||||
#include <list>
|
||||
#include <memory>
|
||||
#include <memory_resource>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
#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<double> 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 <typename T>
|
||||
std::shared_ptr<T> make_two_allocation_shared(std::pmr::memory_resource* resource) {
|
||||
std::pmr::polymorphic_allocator<T> object_allocator(resource);
|
||||
T* object = object_allocator.allocate(1);
|
||||
try {
|
||||
std::allocator_traits<decltype(object_allocator)>::construct(object_allocator, object);
|
||||
}
|
||||
catch (...) {
|
||||
object_allocator.deallocate(object, 1);
|
||||
throw;
|
||||
}
|
||||
auto deleter = [object_allocator](T* value) mutable {
|
||||
std::allocator_traits<decltype(object_allocator)>::destroy(object_allocator, value);
|
||||
object_allocator.deallocate(value, 1);
|
||||
};
|
||||
return std::shared_ptr<T>(object, std::move(deleter), std::pmr::polymorphic_allocator<T>(resource));
|
||||
}
|
||||
template <typename Handler>
|
||||
double measure_milliseconds(Handler handler) {
|
||||
auto begin = std::chrono::steady_clock::now();
|
||||
handler();
|
||||
auto end = std::chrono::steady_clock::now();
|
||||
return std::chrono::duration<double, std::milli>(end - begin).count();
|
||||
}
|
||||
double benchmark_renderables(std::pmr::memory_resource* resource, bool two_allocations) {
|
||||
return measure_milliseconds([resource, two_allocations]() {
|
||||
std::pmr::vector<std::shared_ptr<renderive::Renderable>> owners(resource);
|
||||
owners.reserve(20000);
|
||||
for (int i = 0; i < 20000; ++i) {
|
||||
if (two_allocations)
|
||||
owners.push_back(make_two_allocation_shared<renderive::Renderable>(resource));
|
||||
else
|
||||
owners.push_back(std::allocate_shared<renderive::Renderable>(std::pmr::polymorphic_allocator<renderive::Renderable>(resource)));
|
||||
}
|
||||
benchmark_sink += owners.size();
|
||||
owners.clear();
|
||||
});
|
||||
}
|
||||
double benchmark_waterfall(std::pmr::memory_resource* resource) {
|
||||
static constexpr std::array<int, 5> frequency_sizes{64, 512, 8192, 8193, 128};
|
||||
static constexpr std::array<int, 5> time_sizes{32, 96, 48, 64, 24};
|
||||
return measure_milliseconds([resource]() {
|
||||
for (int round = 0; round < 32; ++round) {
|
||||
std::pmr::vector<std::uint8_t> write_buffer(resource);
|
||||
std::pmr::vector<std::uint8_t> record_buffer(resource);
|
||||
std::pmr::vector<std::uint8_t> snapshot_buffer(resource);
|
||||
std::pmr::vector<double> row_buffer(resource);
|
||||
std::pmr::vector<Benchmark_Waterfall_Row> pending_rows(resource);
|
||||
for (std::size_t stage = 0; stage < frequency_sizes.size(); ++stage) {
|
||||
std::size_t row_bytes = static_cast<std::size_t>(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<std::size_t>(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<std::uint8_t>(round);
|
||||
row_buffer.front() = static_cast<double>(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<int, 4> point_sizes{32, 256, 4096, 64};
|
||||
return measure_milliseconds([resource]() {
|
||||
for (int round = 0; round < 64; ++round) {
|
||||
std::pmr::vector<QTime> time_snapshot(resource);
|
||||
std::pmr::vector<double> power_snapshot(resource);
|
||||
std::pmr::list<Benchmark_Time_Tick> time_ticks(resource);
|
||||
for (int size : point_sizes) {
|
||||
time_snapshot.resize(size);
|
||||
power_snapshot.resize(size);
|
||||
for (int tick = 0; tick < size; ++tick)
|
||||
time_ticks.push_front({QTime::fromMSecsSinceStartOfDay(tick), tick});
|
||||
while (time_ticks.size() > static_cast<std::size_t>(size))
|
||||
time_ticks.pop_back();
|
||||
if (!power_snapshot.empty())
|
||||
power_snapshot.back() = static_cast<double>(round);
|
||||
}
|
||||
benchmark_sink += time_snapshot.size() + power_snapshot.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<PROCESS_MEMORY_COUNTERS*>(&counters), sizeof(counters));
|
||||
return counters.PeakWorkingSetSize;
|
||||
#else
|
||||
rusage usage{};
|
||||
getrusage(RUSAGE_SELF, &usage);
|
||||
return static_cast<std::uint64_t>(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<std::pmr::synchronized_pool_resource> 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<std::pmr::synchronized_pool_resource>(&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<std::pmr::synchronized_pool_resource>(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;
|
||||
}
|
||||
Reference in New Issue
Block a user