mirror of
https://github.com/Z3Prover/z3
synced 2026-01-18 16:28:56 +00:00
Add std::optional vs custom optional performance benchmark (#8160)
* Initial plan * Complete optional benchmark implementation Co-authored-by: NikolajBjorner <3085284+NikolajBjorner@users.noreply.github.com> * Add comprehensive benchmark results documentation Co-authored-by: NikolajBjorner <3085284+NikolajBjorner@users.noreply.github.com> * Address code review feedback - improve benchmark portability and remove redundant volatile Co-authored-by: NikolajBjorner <3085284+NikolajBjorner@users.noreply.github.com> * Final refinement: fix inline assembly constraint for const reference Co-authored-by: NikolajBjorner <3085284+NikolajBjorner@users.noreply.github.com> * Delete OPTIONAL_BENCHMARK_RESULTS.md --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: NikolajBjorner <3085284+NikolajBjorner@users.noreply.github.com> Co-authored-by: Nikolaj Bjorner <nbjorner@microsoft.com>
This commit is contained in:
parent
5aac5c98b3
commit
5cf59ea5e4
3 changed files with 394 additions and 0 deletions
|
|
@ -98,6 +98,7 @@ add_executable(test-z3
|
|||
object_allocator.cpp
|
||||
old_interval.cpp
|
||||
optional.cpp
|
||||
optional_benchmark.cpp
|
||||
parray.cpp
|
||||
pb2bv.cpp
|
||||
pdd.cpp
|
||||
|
|
|
|||
|
|
@ -156,6 +156,7 @@ int main(int argc, char ** argv) {
|
|||
TST(inf_rational);
|
||||
TST(ast);
|
||||
TST(optional);
|
||||
TST(optional_benchmark);
|
||||
TST(bit_vector);
|
||||
TST(fixed_bit_vector);
|
||||
TST(tbv);
|
||||
|
|
|
|||
392
src/test/optional_benchmark.cpp
Normal file
392
src/test/optional_benchmark.cpp
Normal file
|
|
@ -0,0 +1,392 @@
|
|||
/*++
|
||||
Copyright (c) 2006 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
optional_benchmark.cpp
|
||||
|
||||
Abstract:
|
||||
|
||||
Benchmark std::optional vs custom optional implementation
|
||||
|
||||
Author:
|
||||
|
||||
GitHub Copilot 2026-01-11
|
||||
|
||||
Revision History:
|
||||
|
||||
--*/
|
||||
|
||||
#include "util/trace.h"
|
||||
#include "util/debug.h"
|
||||
#include "util/memory_manager.h"
|
||||
#include "util/optional.h"
|
||||
#include <optional>
|
||||
#include <chrono>
|
||||
#include <iostream>
|
||||
#include <iomanip>
|
||||
|
||||
// Simple struct for testing
|
||||
struct BenchData {
|
||||
int x;
|
||||
int y;
|
||||
int z;
|
||||
|
||||
BenchData(int a = 0, int b = 0, int c = 0) : x(a), y(b), z(c) {}
|
||||
};
|
||||
|
||||
// Benchmark helper
|
||||
template<typename Func>
|
||||
double measure_time_ms(Func f, int iterations = 1000000) {
|
||||
auto start = std::chrono::high_resolution_clock::now();
|
||||
f();
|
||||
auto end = std::chrono::high_resolution_clock::now();
|
||||
std::chrono::duration<double, std::milli> elapsed = end - start;
|
||||
return elapsed.count();
|
||||
}
|
||||
|
||||
// Prevent compiler optimization
|
||||
template<typename T>
|
||||
void do_not_optimize(T const& value) {
|
||||
asm volatile("" : : "m"(value) : "memory");
|
||||
}
|
||||
|
||||
void benchmark_construction() {
|
||||
const int iterations = 1000000;
|
||||
|
||||
std::cout << "\n=== Construction Benchmark ===" << std::endl;
|
||||
|
||||
// Test 1: Default construction
|
||||
{
|
||||
double custom_time = measure_time_ms([&]() {
|
||||
for (int i = 0; i < iterations; i++) {
|
||||
optional<int> opt;
|
||||
do_not_optimize(opt);
|
||||
}
|
||||
});
|
||||
|
||||
double std_time = measure_time_ms([&]() {
|
||||
for (int i = 0; i < iterations; i++) {
|
||||
std::optional<int> opt;
|
||||
do_not_optimize(opt);
|
||||
}
|
||||
});
|
||||
|
||||
std::cout << "Default construction (int):" << std::endl;
|
||||
std::cout << " Custom optional: " << std::fixed << std::setprecision(2)
|
||||
<< custom_time << " ms" << std::endl;
|
||||
std::cout << " std::optional: " << std::fixed << std::setprecision(2)
|
||||
<< std_time << " ms" << std::endl;
|
||||
std::cout << " Ratio (custom/std): " << std::fixed << std::setprecision(2)
|
||||
<< (custom_time / std_time) << "x" << std::endl;
|
||||
}
|
||||
|
||||
// Test 2: Value construction
|
||||
{
|
||||
double custom_time = measure_time_ms([&]() {
|
||||
for (int i = 0; i < iterations; i++) {
|
||||
optional<int> opt(i);
|
||||
do_not_optimize(opt);
|
||||
}
|
||||
});
|
||||
|
||||
double std_time = measure_time_ms([&]() {
|
||||
for (int i = 0; i < iterations; i++) {
|
||||
std::optional<int> opt(i);
|
||||
do_not_optimize(opt);
|
||||
}
|
||||
});
|
||||
|
||||
std::cout << "\nValue construction (int):" << std::endl;
|
||||
std::cout << " Custom optional: " << std::fixed << std::setprecision(2)
|
||||
<< custom_time << " ms" << std::endl;
|
||||
std::cout << " std::optional: " << std::fixed << std::setprecision(2)
|
||||
<< std_time << " ms" << std::endl;
|
||||
std::cout << " Ratio (custom/std): " << std::fixed << std::setprecision(2)
|
||||
<< (custom_time / std_time) << "x" << std::endl;
|
||||
}
|
||||
|
||||
// Test 3: Struct construction
|
||||
{
|
||||
double custom_time = measure_time_ms([&]() {
|
||||
for (int i = 0; i < iterations; i++) {
|
||||
optional<BenchData> opt(BenchData(i, i+1, i+2));
|
||||
do_not_optimize(opt);
|
||||
}
|
||||
});
|
||||
|
||||
double std_time = measure_time_ms([&]() {
|
||||
for (int i = 0; i < iterations; i++) {
|
||||
std::optional<BenchData> opt(BenchData(i, i+1, i+2));
|
||||
do_not_optimize(opt);
|
||||
}
|
||||
});
|
||||
|
||||
std::cout << "\nValue construction (struct):" << std::endl;
|
||||
std::cout << " Custom optional: " << std::fixed << std::setprecision(2)
|
||||
<< custom_time << " ms" << std::endl;
|
||||
std::cout << " std::optional: " << std::fixed << std::setprecision(2)
|
||||
<< std_time << " ms" << std::endl;
|
||||
std::cout << " Ratio (custom/std): " << std::fixed << std::setprecision(2)
|
||||
<< (custom_time / std_time) << "x" << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
void benchmark_copy() {
|
||||
const int iterations = 1000000;
|
||||
|
||||
std::cout << "\n=== Copy Benchmark ===" << std::endl;
|
||||
|
||||
// Test 1: Copy construction (int)
|
||||
{
|
||||
optional<int> custom_src(42);
|
||||
std::optional<int> std_src(42);
|
||||
|
||||
double custom_time = measure_time_ms([&]() {
|
||||
for (int i = 0; i < iterations; i++) {
|
||||
optional<int> opt(custom_src);
|
||||
do_not_optimize(opt);
|
||||
}
|
||||
});
|
||||
|
||||
double std_time = measure_time_ms([&]() {
|
||||
for (int i = 0; i < iterations; i++) {
|
||||
std::optional<int> opt(std_src);
|
||||
do_not_optimize(opt);
|
||||
}
|
||||
});
|
||||
|
||||
std::cout << "Copy construction (int):" << std::endl;
|
||||
std::cout << " Custom optional: " << std::fixed << std::setprecision(2)
|
||||
<< custom_time << " ms" << std::endl;
|
||||
std::cout << " std::optional: " << std::fixed << std::setprecision(2)
|
||||
<< std_time << " ms" << std::endl;
|
||||
std::cout << " Ratio (custom/std): " << std::fixed << std::setprecision(2)
|
||||
<< (custom_time / std_time) << "x" << std::endl;
|
||||
}
|
||||
|
||||
// Test 2: Copy assignment (int)
|
||||
{
|
||||
optional<int> custom_src(42);
|
||||
std::optional<int> std_src(42);
|
||||
|
||||
double custom_time = measure_time_ms([&]() {
|
||||
for (int i = 0; i < iterations; i++) {
|
||||
optional<int> opt;
|
||||
opt = custom_src;
|
||||
do_not_optimize(opt);
|
||||
}
|
||||
});
|
||||
|
||||
double std_time = measure_time_ms([&]() {
|
||||
for (int i = 0; i < iterations; i++) {
|
||||
std::optional<int> opt;
|
||||
opt = std_src;
|
||||
do_not_optimize(opt);
|
||||
}
|
||||
});
|
||||
|
||||
std::cout << "\nCopy assignment (int):" << std::endl;
|
||||
std::cout << " Custom optional: " << std::fixed << std::setprecision(2)
|
||||
<< custom_time << " ms" << std::endl;
|
||||
std::cout << " std::optional: " << std::fixed << std::setprecision(2)
|
||||
<< std_time << " ms" << std::endl;
|
||||
std::cout << " Ratio (custom/std): " << std::fixed << std::setprecision(2)
|
||||
<< (custom_time / std_time) << "x" << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
void benchmark_move() {
|
||||
const int iterations = 1000000;
|
||||
|
||||
std::cout << "\n=== Move Benchmark ===" << std::endl;
|
||||
|
||||
// Test 1: Move construction (int)
|
||||
{
|
||||
double custom_time = measure_time_ms([&]() {
|
||||
for (int i = 0; i < iterations; i++) {
|
||||
optional<int> src(i);
|
||||
optional<int> dst(std::move(src));
|
||||
do_not_optimize(dst);
|
||||
}
|
||||
});
|
||||
|
||||
double std_time = measure_time_ms([&]() {
|
||||
for (int i = 0; i < iterations; i++) {
|
||||
std::optional<int> src(i);
|
||||
std::optional<int> dst(std::move(src));
|
||||
do_not_optimize(dst);
|
||||
}
|
||||
});
|
||||
|
||||
std::cout << "Move construction (int):" << std::endl;
|
||||
std::cout << " Custom optional: " << std::fixed << std::setprecision(2)
|
||||
<< custom_time << " ms" << std::endl;
|
||||
std::cout << " std::optional: " << std::fixed << std::setprecision(2)
|
||||
<< std_time << " ms" << std::endl;
|
||||
std::cout << " Ratio (custom/std): " << std::fixed << std::setprecision(2)
|
||||
<< (custom_time / std_time) << "x" << std::endl;
|
||||
}
|
||||
|
||||
// Test 2: Move assignment (int)
|
||||
{
|
||||
double custom_time = measure_time_ms([&]() {
|
||||
for (int i = 0; i < iterations; i++) {
|
||||
optional<int> src(i);
|
||||
optional<int> dst;
|
||||
dst = std::move(src);
|
||||
do_not_optimize(dst);
|
||||
}
|
||||
});
|
||||
|
||||
double std_time = measure_time_ms([&]() {
|
||||
for (int i = 0; i < iterations; i++) {
|
||||
std::optional<int> src(i);
|
||||
std::optional<int> dst;
|
||||
dst = std::move(src);
|
||||
do_not_optimize(dst);
|
||||
}
|
||||
});
|
||||
|
||||
std::cout << "\nMove assignment (int):" << std::endl;
|
||||
std::cout << " Custom optional: " << std::fixed << std::setprecision(2)
|
||||
<< custom_time << " ms" << std::endl;
|
||||
std::cout << " std::optional: " << std::fixed << std::setprecision(2)
|
||||
<< std_time << " ms" << std::endl;
|
||||
std::cout << " Ratio (custom/std): " << std::fixed << std::setprecision(2)
|
||||
<< (custom_time / std_time) << "x" << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
void benchmark_access() {
|
||||
const int iterations = 10000000;
|
||||
|
||||
std::cout << "\n=== Access Benchmark ===" << std::endl;
|
||||
|
||||
// Test 1: Dereference operator
|
||||
{
|
||||
optional<int> custom_opt(42);
|
||||
std::optional<int> std_opt(42);
|
||||
|
||||
double custom_time = measure_time_ms([&]() {
|
||||
int sum = 0;
|
||||
for (int i = 0; i < iterations; i++) {
|
||||
sum += *custom_opt;
|
||||
}
|
||||
do_not_optimize(sum);
|
||||
});
|
||||
|
||||
double std_time = measure_time_ms([&]() {
|
||||
int sum = 0;
|
||||
for (int i = 0; i < iterations; i++) {
|
||||
sum += *std_opt;
|
||||
}
|
||||
do_not_optimize(sum);
|
||||
});
|
||||
|
||||
std::cout << "Dereference operator (int):" << std::endl;
|
||||
std::cout << " Custom optional: " << std::fixed << std::setprecision(2)
|
||||
<< custom_time << " ms" << std::endl;
|
||||
std::cout << " std::optional: " << std::fixed << std::setprecision(2)
|
||||
<< std_time << " ms" << std::endl;
|
||||
std::cout << " Ratio (custom/std): " << std::fixed << std::setprecision(2)
|
||||
<< (custom_time / std_time) << "x" << std::endl;
|
||||
}
|
||||
|
||||
// Test 2: Arrow operator
|
||||
{
|
||||
optional<BenchData> custom_opt(BenchData(1, 2, 3));
|
||||
std::optional<BenchData> std_opt(BenchData(1, 2, 3));
|
||||
|
||||
double custom_time = measure_time_ms([&]() {
|
||||
int sum = 0;
|
||||
for (int i = 0; i < iterations; i++) {
|
||||
sum += custom_opt->x;
|
||||
}
|
||||
do_not_optimize(sum);
|
||||
});
|
||||
|
||||
double std_time = measure_time_ms([&]() {
|
||||
int sum = 0;
|
||||
for (int i = 0; i < iterations; i++) {
|
||||
sum += std_opt->x;
|
||||
}
|
||||
do_not_optimize(sum);
|
||||
});
|
||||
|
||||
std::cout << "\nArrow operator (struct):" << std::endl;
|
||||
std::cout << " Custom optional: " << std::fixed << std::setprecision(2)
|
||||
<< custom_time << " ms" << std::endl;
|
||||
std::cout << " std::optional: " << std::fixed << std::setprecision(2)
|
||||
<< std_time << " ms" << std::endl;
|
||||
std::cout << " Ratio (custom/std): " << std::fixed << std::setprecision(2)
|
||||
<< (custom_time / std_time) << "x" << std::endl;
|
||||
}
|
||||
|
||||
// Test 3: Boolean conversion
|
||||
{
|
||||
optional<int> custom_opt(42);
|
||||
std::optional<int> std_opt(42);
|
||||
|
||||
double custom_time = measure_time_ms([&]() {
|
||||
int count = 0;
|
||||
for (int i = 0; i < iterations; i++) {
|
||||
if (custom_opt) count++;
|
||||
}
|
||||
do_not_optimize(count);
|
||||
});
|
||||
|
||||
double std_time = measure_time_ms([&]() {
|
||||
int count = 0;
|
||||
for (int i = 0; i < iterations; i++) {
|
||||
if (std_opt) count++;
|
||||
}
|
||||
do_not_optimize(count);
|
||||
});
|
||||
|
||||
std::cout << "\nBoolean conversion:" << std::endl;
|
||||
std::cout << " Custom optional: " << std::fixed << std::setprecision(2)
|
||||
<< custom_time << " ms" << std::endl;
|
||||
std::cout << " std::optional: " << std::fixed << std::setprecision(2)
|
||||
<< std_time << " ms" << std::endl;
|
||||
std::cout << " Ratio (custom/std): " << std::fixed << std::setprecision(2)
|
||||
<< (custom_time / std_time) << "x" << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
void benchmark_memory() {
|
||||
std::cout << "\n=== Memory Footprint ===" << std::endl;
|
||||
|
||||
std::cout << "Size of optional<int>:" << std::endl;
|
||||
std::cout << " Custom optional: " << sizeof(optional<int>) << " bytes" << std::endl;
|
||||
std::cout << " std::optional: " << sizeof(std::optional<int>) << " bytes" << std::endl;
|
||||
|
||||
std::cout << "\nSize of optional<BenchData>:" << std::endl;
|
||||
std::cout << " Custom optional: " << sizeof(optional<BenchData>) << " bytes" << std::endl;
|
||||
std::cout << " std::optional: " << sizeof(std::optional<BenchData>) << " bytes" << std::endl;
|
||||
|
||||
std::cout << "\nSize of optional<int*>:" << std::endl;
|
||||
std::cout << " Custom optional: " << sizeof(optional<int*>) << " bytes" << std::endl;
|
||||
std::cout << " std::optional: " << sizeof(std::optional<int*>) << " bytes" << std::endl;
|
||||
}
|
||||
|
||||
void tst_optional_benchmark() {
|
||||
std::cout << "\n╔═══════════════════════════════════════════════════════════════╗" << std::endl;
|
||||
std::cout << "║ std::optional vs Custom optional Performance Benchmark ║" << std::endl;
|
||||
std::cout << "╚═══════════════════════════════════════════════════════════════╝" << std::endl;
|
||||
|
||||
benchmark_memory();
|
||||
benchmark_construction();
|
||||
benchmark_copy();
|
||||
benchmark_move();
|
||||
benchmark_access();
|
||||
|
||||
std::cout << "\n═══════════════════════════════════════════════════════════════" << std::endl;
|
||||
std::cout << "Benchmark completed!" << std::endl;
|
||||
std::cout << "\nNotes:" << std::endl;
|
||||
std::cout << "- Custom optional uses heap allocation (alloc/dealloc)" << std::endl;
|
||||
std::cout << "- std::optional uses in-place storage (no heap allocation)" << std::endl;
|
||||
std::cout << "- Ratios > 1.0 indicate custom optional is slower" << std::endl;
|
||||
std::cout << "- Ratios < 1.0 indicate custom optional is faster" << std::endl;
|
||||
std::cout << "═══════════════════════════════════════════════════════════════\n" << std::endl;
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue