Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Generate benchmarking report #181

Merged
merged 2 commits into from
Oct 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 9 additions & 2 deletions .github/workflows/bvt-appleclang.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ jobs:
bvt-appleclang:
runs-on: macos-15
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
with:
ref: ${{ inputs.branch }}

Expand All @@ -26,4 +26,11 @@ jobs:

- name: run benchmarks
run: |
./build/benchmarks/msft_proxy_benchmarks --benchmark_repetitions=100 --benchmark_report_aggregates_only=true --benchmark_min_time=0.1s --benchmark_enable_random_interleaving=true
cd build/benchmarks
./msft_proxy_benchmarks --benchmark_repetitions=10 --benchmark_report_aggregates_only=true --benchmark_enable_random_interleaving=true --benchmark_out=benchmarking-results.json

- name: archive benchmarking results
uses: actions/upload-artifact@v4
with:
name: benchmarking-results-appleclang
path: build/benchmarks/benchmarking-results.json
11 changes: 9 additions & 2 deletions .github/workflows/bvt-clang.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ jobs:
bvt-clang:
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
with:
ref: ${{ inputs.branch }}

Expand Down Expand Up @@ -50,4 +50,11 @@ jobs:

- name: run benchmarks
run: |
./build-clang-18/benchmarks/msft_proxy_benchmarks --benchmark_repetitions=100 --benchmark_report_aggregates_only=true --benchmark_min_time=0.1s --benchmark_enable_random_interleaving=true
cd build-clang-18/benchmarks
./msft_proxy_benchmarks --benchmark_repetitions=10 --benchmark_report_aggregates_only=true --benchmark_enable_random_interleaving=true --benchmark_out=benchmarking-results.json

- name: archive benchmarking results
uses: actions/upload-artifact@v4
with:
name: benchmarking-results-clang
path: build-clang-18/benchmarks/benchmarking-results.json
11 changes: 9 additions & 2 deletions .github/workflows/bvt-gcc.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ jobs:
bvt-gcc:
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
with:
ref: ${{ inputs.branch }}

Expand Down Expand Up @@ -50,4 +50,11 @@ jobs:

- name: run benchmarks
run: |
./build-gcc-14/benchmarks/msft_proxy_benchmarks --benchmark_repetitions=100 --benchmark_report_aggregates_only=true --benchmark_min_time=0.1s --benchmark_enable_random_interleaving=true
cd build-gcc-14/benchmarks
./msft_proxy_benchmarks --benchmark_repetitions=10 --benchmark_report_aggregates_only=true --benchmark_enable_random_interleaving=true --benchmark_out=benchmarking-results.json

- name: archive benchmarking results
uses: actions/upload-artifact@v4
with:
name: benchmarking-results-gcc
path: build-gcc-14/benchmarks/benchmarking-results.json
11 changes: 9 additions & 2 deletions .github/workflows/bvt-msvc.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ jobs:
bvt-msvc:
runs-on: windows-2022
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
with:
ref: ${{ inputs.branch }}

Expand All @@ -21,4 +21,11 @@ jobs:

- name: run benchmarks
run: |
.\build\benchmarks\Release\msft_proxy_benchmarks.exe --benchmark_repetitions=100 --benchmark_report_aggregates_only=true --benchmark_min_time=0.1s --benchmark_enable_random_interleaving=true
cd build\benchmarks
.\Release\msft_proxy_benchmarks.exe --benchmark_repetitions=10 --benchmark_report_aggregates_only=true --benchmark_enable_random_interleaving=true --benchmark_out=benchmarking-results.json

- name: archive benchmarking results
uses: actions/upload-artifact@v4
with:
name: benchmarking-results-msvc
path: build/benchmarks/benchmarking-results.json
35 changes: 35 additions & 0 deletions .github/workflows/bvt-report.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
on:
workflow_call:
inputs:
branch:
type: string
required: false

jobs:
bvt-report:
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v4
with:
ref: ${{ inputs.branch }}

- name: download all workflow run artifacts
uses: actions/download-artifact@v4
with:
path: artifacts

- name: build report generator
run: |
cd tools/report_generator
cmake -B build -DCMAKE_BUILD_TYPE=Release
cmake --build build -j

- name: generate report
run: |
tools/report_generator/build/report_generator tools/report_generator/report-config.json ${{ github.sha }} artifacts benchmarking-report.md

- name: archive benchmarking report
uses: actions/upload-artifact@v4
with:
name: benchmarking-report
path: benchmarking-report.md
9 changes: 5 additions & 4 deletions .github/workflows/pipeline-ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,6 @@ on:
pull_request:
branches: [ main, release/** ]

env:
# Customize the CMake build type here (Release, Debug, RelWithDebInfo, etc.)
BUILD_TYPE: Release

jobs:
run-bvt-gcc:
uses: ./.github/workflows/bvt-gcc.yml
Expand All @@ -26,3 +22,8 @@ jobs:
run-bvt-appleclang:
uses: ./.github/workflows/bvt-appleclang.yml
name: Run BVT with AppleClang

report:
uses: ./.github/workflows/bvt-report.yml
name: Generate report
needs: [run-bvt-gcc, run-bvt-clang, run-bvt-msvc, run-bvt-appleclang]
5 changes: 5 additions & 0 deletions .github/workflows/pipeline-release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,11 @@ jobs:
with:
branch: release/${{ github.event.inputs.version }}

report:
uses: ./.github/workflows/bvt-report.yml
name: Generate report
needs: [run-bvt-gcc, run-bvt-clang, run-bvt-msvc, run-bvt-appleclang]

draft-release:
runs-on: windows-latest
needs: [run-bvt-gcc, run-bvt-clang, run-bvt-msvc, run-bvt-appleclang]
Expand Down
6 changes: 3 additions & 3 deletions benchmarks/proxy_management_benchmark.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ void BM_SmallObjectManagementWithSharedPtr(benchmark::State& state) {
}

void BM_SmallObjectManagementWithSharedPtr_Pooled(benchmark::State& state) {
std::pmr::unsynchronized_pool_resource pool;
static std::pmr::unsynchronized_pool_resource pool;
std::pmr::polymorphic_allocator<> alloc{&pool};
for (auto _ : state) {
std::vector<std::shared_ptr<void>> data;
Expand Down Expand Up @@ -128,7 +128,7 @@ void BM_LargeObjectManagementWithProxy(benchmark::State& state) {
}

void BM_LargeObjectManagementWithProxy_Pooled(benchmark::State& state) {
std::pmr::unsynchronized_pool_resource pool;
static std::pmr::unsynchronized_pool_resource pool;
std::pmr::polymorphic_allocator<> alloc{&pool};
for (auto _ : state) {
std::vector<pro::proxy<DefaultFacade>> data;
Expand Down Expand Up @@ -169,7 +169,7 @@ void BM_LargeObjectManagementWithSharedPtr(benchmark::State& state) {
}

void BM_LargeObjectManagementWithSharedPtr_Pooled(benchmark::State& state) {
std::pmr::unsynchronized_pool_resource pool;
static std::pmr::unsynchronized_pool_resource pool;
std::pmr::polymorphic_allocator<> alloc{&pool};
for (auto _ : state) {
std::vector<std::shared_ptr<void>> data;
Expand Down
28 changes: 28 additions & 0 deletions tools/report_generator/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
cmake_minimum_required(VERSION 3.5)

project(report_generator)

include(FetchContent)
# The policy uses the download time for timestamp, instead of the timestamp in the archive. This
# allows for proper rebuilds when a projects URL changes.
if(POLICY CMP0135)
cmake_policy(SET CMP0135 NEW)
endif()

FetchContent_Declare(
nlohmann_json
URL https://github.com/nlohmann/json/releases/download/v3.11.3/json.tar.xz
URL_HASH SHA256=d6c65aca6b1ed68e7a182f4757257b107ae403032760ed6ef121c9d55e81757d
)
FetchContent_MakeAvailable(nlohmann_json)

add_executable(report_generator main.cpp)

target_link_libraries(report_generator PRIVATE nlohmann_json::nlohmann_json)

target_compile_features(report_generator PRIVATE cxx_std_20)
if (MSVC)
target_compile_options(report_generator PRIVATE /W4 /WX)
else()
target_compile_options(report_generator PRIVATE -Wall -Wextra -Wpedantic -Werror)
endif()
176 changes: 176 additions & 0 deletions tools/report_generator/main.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,176 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

#include <cstdio>
#include <chrono>
#include <filesystem>
#include <format>
#include <fstream>
#include <iostream>
#include <string_view>
#include <string>
#include <unordered_map>
#include <vector>

#include <nlohmann/json.hpp>

struct EnvironmentInfo {
std::string Name;
std::string Description;

friend void to_json(nlohmann::json& j, const EnvironmentInfo& e) {
j = nlohmann::json{
{"Name", e.Name},
{"Description", e.Description}
};
}

friend void from_json(const nlohmann::json& j, EnvironmentInfo& e) {
j.at("Name").get_to(e.Name);
j.at("Description").get_to(e.Description);
}
};

struct MetricInfo {
std::string Name;
std::string TargetBenchmarkName;
std::string BaselineBenchmarkName;

friend void to_json(nlohmann::json& j, const MetricInfo& m) {
j = nlohmann::json{
{"Name", m.Name},
{"TargetBenchmarkName", m.TargetBenchmarkName},
{"BaselineBenchmarkName", m.BaselineBenchmarkName}
};
}

friend void from_json(const nlohmann::json& j, MetricInfo& m) {
j.at("Name").get_to(m.Name);
j.at("TargetBenchmarkName").get_to(m.TargetBenchmarkName);
j.at("BaselineBenchmarkName").get_to(m.BaselineBenchmarkName);
}
};

struct ReportConfig {
std::string TargetName;
double YellowIndicatorThreshold;
std::vector<EnvironmentInfo> Environments;
std::vector<MetricInfo> Metrics;

friend void to_json(nlohmann::json& j, const ReportConfig& rc) {
j = nlohmann::json{
{"TargetName", rc.TargetName},
{"YellowIndicatorThreshold", rc.YellowIndicatorThreshold},
{"Environments", rc.Environments},
{"Metrics", rc.Metrics}
};
}

friend void from_json(const nlohmann::json& j, ReportConfig& rc) {
j.at("TargetName").get_to(rc.TargetName);
j.at("YellowIndicatorThreshold").get_to(rc.YellowIndicatorThreshold);
j.at("Environments").get_to(rc.Environments);
j.at("Metrics").get_to(rc.Metrics);
}
};

const std::string_view MedianSuffix = "_median";

std::unordered_map<std::string, double> Parse(const std::filesystem::path& file) {
nlohmann::json obj;
{
std::ifstream in;
in.exceptions(std::ios_base::failbit | std::ios_base::badbit);
in.open(file, std::ios_base::in | std::ios_base::binary);
in >> obj;
}
std::unordered_map<std::string, double> result;
for (auto& node : obj["benchmarks"]) {
std::string name = node["name"];
if (name.ends_with(MedianSuffix)) {
name.resize(name.size() - MedianSuffix.size());
double value = node["real_time"];
result.emplace(std::move(name), value);
}
}
return result;
}

void GenerateReport(const std::filesystem::path& config_path, const std::string& commit_id, const std::filesystem::path& source, const std::filesystem::path& output) {
ReportConfig config;
{
nlohmann::json obj;
std::ifstream in;
in.exceptions(std::ios_base::failbit | std::ios_base::badbit);
in.open(config_path, std::ios_base::in | std::ios_base::binary);
in >> obj;
obj.get_to(config);
}
std::vector<std::unordered_map<std::string, double>> benchmarks;
benchmarks.reserve(config.Environments.size());
for (auto& environment : config.Environments) {
benchmarks.push_back(Parse(source / std::format("benchmarking-results-{}", environment.Name) / "benchmarking-results.json"));
}
std::ofstream out;
out.exceptions(std::ios_base::failbit | std::ios_base::badbit);
out.open(output, std::ios_base::out | std::ios_base::trunc | std::ios_base::binary);
out << "## Benchmarking Report\n";
out << "\n";
out << "- Generated for: [Microsoft \"Proxy\" library](https://github.com/microsoft/proxy)\n";
out << "- Commit ID: [" << commit_id << "](https://github.com/microsoft/proxy/commit/" << commit_id << ")\n";
out << "- Generated at: " << std::format("{:%FT%TZ}", std::chrono::utc_clock::now()) << "\n";
out << "\n";
out << "| |";
for (auto& environment : config.Environments) {
out << " " << environment.Description << " |";
}
out << "\n";
out << "| - |";
for (std::size_t i = 0; i < config.Environments.size(); ++i) {
out << " - |";
}
out << "\n";
for (auto& metric : config.Metrics) {
out << "| " << metric.Name << " |";
for (auto& benchmark : benchmarks) {
double target = benchmark.at(metric.TargetBenchmarkName);
double baseline = benchmark.at(metric.BaselineBenchmarkName);
double rate = (baseline - target) * 100 / target;
bool is_negative = rate < 0;
if (is_negative) {
rate = -rate;
}
out << " ";
if (rate < config.YellowIndicatorThreshold) {
out << "\xf0\x9f\x9f\xa1"; // Yellow circle
} else if (is_negative) {
out << "\xf0\x9f\x94\xb4"; // Red circle
} else {
out << "\xf0\x9f\x9f\xa2"; // Green circle
}
auto rate_str = std::format("{:.1f}", rate);
std::string message;
if (rate_str == "0.0") {
out << config.TargetName << " has similar performance";
} else {
out << config.TargetName << " is about **" << rate_str << "% " << (is_negative ? "slower" : "faster") << "**";
}
out << " |";
}
out << "\n";
}
}

int main(int argc, char** argv) {
if (argc != 5) {
puts("Usage: report_generator <config file path> <commit ID> <benchmarking results directory> <output file path>");
return 0;
}
try {
GenerateReport(argv[1], argv[2], argv[3], argv[4]);
} catch (const std::exception& e) {
fprintf(stderr, "An error occurred: %s\n", e.what());
return 1;
}
return 0;
}
Loading