| // Copyright 2018 The Fuchsia Authors. All rights reserved. | |
| // Use of this source code is governed by a BSD-style license that can be | |
| // found in the LICENSE file. | |
| #include <lib/inspect/cpp/inspect.h> | |
| #include <lib/inspect/cpp/vmo/heap.h> | |
| #include <zircon/syscalls.h> | |
| #include <cmath> | |
| #include <iostream> | |
| #include <sstream> | |
| #include <fbl/ref_ptr.h> | |
| #include <fbl/string_printf.h> | |
| #include <perftest/perftest.h> | |
| #include "src/lib/fxl/strings/string_printf.h" | |
| namespace { | |
| const char* kName = "name"; | |
| const size_t kLinearFloor = 10; | |
| const size_t kLinearStep = 5; | |
| const size_t kExponentialFloor = 10; | |
| const size_t kExponentialInitialStep = 5; | |
| const size_t kExponentialStepMultiplier = 2; | |
| using inspect::Inspector; | |
| using inspect::Node; | |
| using inspect::internal::BlockIndex; | |
| using inspect::internal::Heap; | |
| using inspect::internal::NumericProperty; | |
| template <typename T> | |
| NumericProperty<T> CreateMetric(Node* root); | |
| template <> | |
| NumericProperty<int64_t> CreateMetric<int64_t>(Node* root) { | |
| return root->CreateInt(kName, 0); | |
| } | |
| template <> | |
| NumericProperty<uint64_t> CreateMetric<uint64_t>(Node* root) { | |
| return root->CreateUint(kName, 0); | |
| } | |
| template <> | |
| NumericProperty<double> CreateMetric<double>(Node* root) { | |
| return root->CreateDouble(kName, 0); | |
| } | |
| template <typename T> | |
| T CreateArrayMetric(Node* root, size_t size); | |
| template <> | |
| inspect::IntArray CreateArrayMetric(Node* root, size_t size) { | |
| return root->CreateIntArray(kName, size); | |
| } | |
| template <> | |
| inspect::UintArray CreateArrayMetric(Node* root, size_t size) { | |
| return root->CreateUintArray(kName, size); | |
| } | |
| template <> | |
| inspect::DoubleArray CreateArrayMetric(Node* root, size_t size) { | |
| return root->CreateDoubleArray(kName, size); | |
| } | |
| template <> | |
| inspect::LinearIntHistogram CreateArrayMetric(Node* root, size_t size) { | |
| return root->CreateLinearIntHistogram(kName, kLinearFloor, kLinearStep, size); | |
| } | |
| template <> | |
| inspect::LinearUintHistogram CreateArrayMetric(Node* root, size_t size) { | |
| return root->CreateLinearUintHistogram(kName, kLinearFloor, kLinearStep, size); | |
| } | |
| template <> | |
| inspect::LinearDoubleHistogram CreateArrayMetric(Node* root, size_t size) { | |
| return root->CreateLinearDoubleHistogram(kName, kLinearFloor, kLinearStep, size); | |
| } | |
| template <> | |
| inspect::ExponentialIntHistogram CreateArrayMetric(Node* root, size_t size) { | |
| return root->CreateExponentialIntHistogram(kName, kExponentialFloor, kExponentialInitialStep, | |
| kExponentialStepMultiplier, size); | |
| } | |
| template <> | |
| inspect::ExponentialUintHistogram CreateArrayMetric(Node* root, size_t size) { | |
| return root->CreateExponentialUintHistogram(kName, kExponentialFloor, kExponentialInitialStep, | |
| kExponentialStepMultiplier, size); | |
| } | |
| template <> | |
| inspect::ExponentialDoubleHistogram CreateArrayMetric(Node* root, size_t size) { | |
| return root->CreateExponentialDoubleHistogram(kName, kExponentialFloor, kExponentialInitialStep, | |
| kExponentialStepMultiplier, size); | |
| } | |
| bool TestNodeLifecycle(perftest::RepeatState* state) { | |
| auto inspector = Inspector(); | |
| auto& root = inspector.GetRoot(); | |
| state->DeclareStep("Create"); | |
| state->DeclareStep("Destroy"); | |
| while (state->KeepRunning()) { | |
| auto node = root.CreateChild(kName); | |
| state->NextStep(); | |
| } | |
| return true; | |
| } | |
| bool TestValueListLifecycle(perftest::RepeatState* state) { | |
| struct Dummy { | |
| uint64_t value; | |
| }; | |
| state->DeclareStep("Create"); | |
| state->DeclareStep("Enlist"); | |
| state->DeclareStep("EnlistAgain"); | |
| state->DeclareStep("Destroy"); | |
| while (state->KeepRunning()) { | |
| inspect::ValueList list; | |
| state->NextStep(); | |
| list.emplace(Dummy{.value = 0}); | |
| state->NextStep(); | |
| list.emplace(Dummy{.value = 1}); | |
| state->NextStep(); | |
| } | |
| return true; | |
| } | |
| template <typename T> | |
| bool TestMetricLifecycle(perftest::RepeatState* state) { | |
| auto inspector = Inspector(); | |
| auto& root = inspector.GetRoot(); | |
| state->DeclareStep("Create"); | |
| state->DeclareStep("Destroy"); | |
| while (state->KeepRunning()) { | |
| auto item = CreateMetric<T>(&root); | |
| state->NextStep(); | |
| } | |
| return true; | |
| } | |
| template <typename T> | |
| bool TestArrayLifecycle(perftest::RepeatState* state, size_t size) { | |
| auto inspector = Inspector(); | |
| auto& root = inspector.GetRoot(); | |
| state->DeclareStep("Create"); | |
| state->DeclareStep("Destroy"); | |
| while (state->KeepRunning()) { | |
| auto item = CreateArrayMetric<T>(&root, size); | |
| state->NextStep(); | |
| } | |
| return true; | |
| } | |
| // Measure the time taken to set and modify NumericProperty. | |
| template <typename T> | |
| bool TestMetricModify(perftest::RepeatState* state) { | |
| auto inspector = Inspector(); | |
| auto& root = inspector.GetRoot(); | |
| auto item = CreateMetric<T>(&root); | |
| state->DeclareStep("Set"); | |
| state->DeclareStep("Add"); | |
| state->DeclareStep("Subtract"); | |
| while (state->KeepRunning()) { | |
| item.Set(1); | |
| state->NextStep(); | |
| item.Add(1); | |
| state->NextStep(); | |
| item.Subtract(1); | |
| } | |
| return true; | |
| } | |
| template <typename T> | |
| bool TestArrayModify(perftest::RepeatState* state, int size) { | |
| auto inspector = Inspector(inspect::InspectSettings{.maximum_size = 1024 * 1024}); | |
| auto& root = inspector.GetRoot(); | |
| auto item = CreateArrayMetric<T>(&root, size); | |
| state->DeclareStep("Set"); | |
| state->DeclareStep("Add"); | |
| state->DeclareStep("Subtract"); | |
| int i = 0; | |
| while (state->KeepRunning()) { | |
| item.Set(i, 1); | |
| state->NextStep(); | |
| item.Add(i, 1); | |
| state->NextStep(); | |
| item.Subtract(i, 1); | |
| i = (i + 1) % size; | |
| } | |
| return true; | |
| } | |
| template <typename T> | |
| bool TestHistogramInsert(perftest::RepeatState* state, int size, int value) { | |
| auto inspector = Inspector(inspect::InspectSettings{.maximum_size = 1024 * 1024}); | |
| auto& root = inspector.GetRoot(); | |
| auto item = CreateArrayMetric<T>(&root, size); | |
| const int underflow_value = 0; | |
| const int overflow_value = 10000000; | |
| state->DeclareStep("InsertUnderflow"); | |
| state->DeclareStep("InsertOverflow"); | |
| state->DeclareStep("InsertValue"); | |
| while (state->KeepRunning()) { | |
| item.Insert(underflow_value); | |
| state->NextStep(); | |
| item.Insert(overflow_value); | |
| state->NextStep(); | |
| item.Insert(value); | |
| } | |
| return true; | |
| } | |
| // Measure the time taken to set and modify Property. | |
| bool TestProperty(perftest::RepeatState* state, int size) { | |
| auto inspector = Inspector(inspect::InspectSettings{.maximum_size = 1024 * 1024}); | |
| auto& root = inspector.GetRoot(); | |
| auto item = root.CreateString(kName, ""); | |
| std::string string; | |
| string.resize(size, 'a'); | |
| state->DeclareStep("Create"); | |
| state->DeclareStep("Set"); | |
| state->DeclareStep("SetAgain"); | |
| state->DeclareStep("Destroy"); | |
| while (state->KeepRunning()) { | |
| auto item = root.CreateString(kName, ""); | |
| state->NextStep(); | |
| item.Set(string); | |
| state->NextStep(); | |
| item.Set(string); | |
| state->NextStep(); | |
| } | |
| return true; | |
| } | |
| // Measure how long it takes to allocate and extend a heap. | |
| bool TestHeapExtend(perftest::RepeatState* state) { | |
| zx::vmo vmo; | |
| state->DeclareStep("Create 1MB VMO"); | |
| state->DeclareStep("Allocate 512K"); | |
| state->DeclareStep("Extend"); | |
| state->DeclareStep("Free"); | |
| state->DeclareStep("Destroy"); | |
| while (state->KeepRunning()) { | |
| BlockIndex index[513]; | |
| if (zx::vmo::create(1 << 21, 0, &vmo) != ZX_OK) { | |
| return false; | |
| } | |
| auto heap = Heap(std::move(vmo)); | |
| state->NextStep(); | |
| for (int i = 0; i < 512; i++) { | |
| if (heap.Allocate(2048, &index[i]) != ZX_OK) { | |
| return false; | |
| } | |
| } | |
| state->NextStep(); | |
| if (heap.Allocate(2048, &index[512]) != ZX_OK) { | |
| return false; | |
| } | |
| state->NextStep(); | |
| for (int i = 512; i >= 0; i--) { | |
| heap.Free(index[i]); | |
| } | |
| state->NextStep(); | |
| } | |
| return true; | |
| } | |
| void RegisterTests() { | |
| perftest::RegisterTest("Inspect/ValueList/Lifecycle", TestValueListLifecycle); | |
| perftest::RegisterTest("Inspect/Node/Lifecycle", TestNodeLifecycle); | |
| perftest::RegisterTest("Inspect/IntMetric/Lifecycle", TestMetricLifecycle<int64_t>); | |
| perftest::RegisterTest("Inspect/IntMetric/Modify", TestMetricModify<int64_t>); | |
| perftest::RegisterTest("Inspect/UintMetric/Lifecycle", TestMetricLifecycle<uint64_t>); | |
| perftest::RegisterTest("Inspect/UintMetric/Modify", TestMetricModify<uint64_t>); | |
| perftest::RegisterTest("Inspect/DoubleMetric/Lifecycle", TestMetricLifecycle<double>); | |
| perftest::RegisterTest("Inspect/DoubleMetric/Modify", TestMetricModify<double>); | |
| for (auto size : {32, 128, 240}) { /* stop at 240 to fit in block */ | |
| perftest::RegisterTest(fxl::StringPrintf("Inspect/UintArray/Lifecycle/%d", size).c_str(), | |
| TestArrayLifecycle<inspect::UintArray>, size); | |
| perftest::RegisterTest(fxl::StringPrintf("Inspect/UintArray/Modify/%d", size).c_str(), | |
| TestArrayModify<inspect::UintArray>, size); | |
| perftest::RegisterTest(fxl::StringPrintf("Inspect/IntArray/Lifecycle/%d", size).c_str(), | |
| TestArrayLifecycle<inspect::IntArray>, size); | |
| perftest::RegisterTest(fxl::StringPrintf("Inspect/IntArray/Modify/%d", size).c_str(), | |
| TestArrayModify<inspect::IntArray>, size); | |
| perftest::RegisterTest(fxl::StringPrintf("Inspect/DoubleArray/Lifecycle/%d", size).c_str(), | |
| TestArrayLifecycle<inspect::DoubleArray>, size); | |
| perftest::RegisterTest(fxl::StringPrintf("Inspect/DoubleArray/Modify/%d", size).c_str(), | |
| TestArrayModify<inspect::DoubleArray>, size); | |
| const int linear_midpoint = kLinearFloor + (size / 2) * kLinearStep; | |
| perftest::RegisterTest( | |
| fxl::StringPrintf("Inspect/LinearUintHistogram/Lifecycle/%d", size).c_str(), | |
| TestArrayLifecycle<inspect::LinearUintHistogram>, size); | |
| perftest::RegisterTest(fxl::StringPrintf("Inspect/LinearUintHistogram/Insert/%d", size).c_str(), | |
| TestHistogramInsert<inspect::LinearUintHistogram>, size, | |
| linear_midpoint); | |
| perftest::RegisterTest( | |
| fxl::StringPrintf("Inspect/LinearIntHistogram/Lifecycle/%d", size).c_str(), | |
| TestArrayLifecycle<inspect::LinearIntHistogram>, size); | |
| perftest::RegisterTest(fxl::StringPrintf("Inspect/LinearIntHistogram/Insert/%d", size).c_str(), | |
| TestHistogramInsert<inspect::LinearIntHistogram>, size, linear_midpoint); | |
| perftest::RegisterTest( | |
| fxl::StringPrintf("Inspect/LinearDoubleHistogram/Lifecycle/%d", size).c_str(), | |
| TestArrayLifecycle<inspect::LinearDoubleHistogram>, size); | |
| perftest::RegisterTest( | |
| fxl::StringPrintf("Inspect/LinearDoubleHistogram/Insert/%d", size).c_str(), | |
| TestHistogramInsert<inspect::LinearDoubleHistogram>, size, linear_midpoint); | |
| } | |
| for (auto size : {4, 16, 32}) { | |
| const int exponential_midpoint = | |
| static_cast<int>(kExponentialFloor + | |
| kExponentialInitialStep * std::pow(kExponentialStepMultiplier, size / 2)); | |
| perftest::RegisterTest( | |
| fxl::StringPrintf("Inspect/ExponentialUintHistogram/Lifecycle/%d", size).c_str(), | |
| TestArrayLifecycle<inspect::ExponentialUintHistogram>, size); | |
| perftest::RegisterTest( | |
| fxl::StringPrintf("Inspect/ExponentialUintHistogram/Insert/%d", size).c_str(), | |
| TestHistogramInsert<inspect::ExponentialUintHistogram>, size, exponential_midpoint); | |
| perftest::RegisterTest( | |
| fxl::StringPrintf("Inspect/ExponentialIntHistogram/Lifecycle/%d", size).c_str(), | |
| TestArrayLifecycle<inspect::ExponentialIntHistogram>, size); | |
| perftest::RegisterTest( | |
| fxl::StringPrintf("Inspect/ExponentialIntHistogram/Insert/%d", size).c_str(), | |
| TestHistogramInsert<inspect::ExponentialIntHistogram>, size, exponential_midpoint); | |
| perftest::RegisterTest( | |
| fxl::StringPrintf("Inspect/ExponentialDoubleHistogram/Lifecycle/%d", size).c_str(), | |
| TestArrayLifecycle<inspect::ExponentialDoubleHistogram>, size); | |
| perftest::RegisterTest( | |
| fxl::StringPrintf("Inspect/ExponentialDoubleHistogram/Insert/%d", size).c_str(), | |
| TestHistogramInsert<inspect::ExponentialDoubleHistogram>, size, exponential_midpoint); | |
| } | |
| for (auto size : {4, 8, 100, 2000, 2048, 10000}) { | |
| perftest::RegisterTest(fxl::StringPrintf("Inspect/Property/%d", size).c_str(), TestProperty, | |
| size); | |
| } | |
| perftest::RegisterTest("Inspect/Heap/Extend", TestHeapExtend); | |
| } | |
| PERFTEST_CTOR(RegisterTests); | |
| } // namespace |