mirror of
https://github.com/Z3Prover/z3
synced 2026-05-05 09:55:15 +00:00
Replace custom util/optional with std::optional (#8162)
* Initial plan * Replace optional with std::optional in source files Co-authored-by: NikolajBjorner <3085284+NikolajBjorner@users.noreply.github.com> * Fix array_map contains() and remove optional_benchmark test Co-authored-by: NikolajBjorner <3085284+NikolajBjorner@users.noreply.github.com> * Address code review feedback - simplify array_map and test Co-authored-by: NikolajBjorner <3085284+NikolajBjorner@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: NikolajBjorner <3085284+NikolajBjorner@users.noreply.github.com>
This commit is contained in:
parent
40250bfcb8
commit
4d188f07e9
15 changed files with 34 additions and 592 deletions
|
|
@ -26,7 +26,7 @@ Revision History:
|
||||||
#include "util/symbol.h"
|
#include "util/symbol.h"
|
||||||
#include "util/rational.h"
|
#include "util/rational.h"
|
||||||
#include "util/hash.h"
|
#include "util/hash.h"
|
||||||
#include "util/optional.h"
|
#include <optional>
|
||||||
#include "util/trace.h"
|
#include "util/trace.h"
|
||||||
#include "util/bit_vector.h"
|
#include "util/bit_vector.h"
|
||||||
#include "util/symbol_table.h"
|
#include "util/symbol_table.h"
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,7 @@ Author:
|
||||||
|
|
||||||
#include "util/obj_pair_set.h"
|
#include "util/obj_pair_set.h"
|
||||||
#include "util/checked_int64.h"
|
#include "util/checked_int64.h"
|
||||||
#include "util/optional.h"
|
#include <optional>
|
||||||
#include "ast/ast_trail.h"
|
#include "ast/ast_trail.h"
|
||||||
#include "ast/arith_decl_plugin.h"
|
#include "ast/arith_decl_plugin.h"
|
||||||
#include "ast/sls/sls_context.h"
|
#include "ast/sls/sls_context.h"
|
||||||
|
|
@ -115,7 +115,7 @@ namespace sls {
|
||||||
sat::bool_var_vector m_bool_vars_of;
|
sat::bool_var_vector m_bool_vars_of;
|
||||||
unsigned_vector m_clauses_of;
|
unsigned_vector m_clauses_of;
|
||||||
unsigned_vector m_muls, m_adds, m_ops, m_ifs;
|
unsigned_vector m_muls, m_adds, m_ops, m_ifs;
|
||||||
optional<bound> m_lo, m_hi;
|
std::optional<bound> m_lo, m_hi;
|
||||||
vector<num_t> m_finite_domain;
|
vector<num_t> m_finite_domain;
|
||||||
|
|
||||||
num_t const& value() const { return m_value; }
|
num_t const& value() const { return m_value; }
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,7 @@ Author:
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "util/checked_int64.h"
|
#include "util/checked_int64.h"
|
||||||
#include "util/optional.h"
|
#include <optional>
|
||||||
#include "util/nat_set.h"
|
#include "util/nat_set.h"
|
||||||
#include "ast/ast_trail.h"
|
#include "ast/ast_trail.h"
|
||||||
#include "ast/arith_decl_plugin.h"
|
#include "ast/arith_decl_plugin.h"
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,7 @@ Author:
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "util/checked_int64.h"
|
#include "util/checked_int64.h"
|
||||||
#include "util/optional.h"
|
#include <optional>
|
||||||
#include "util/nat_set.h"
|
#include "util/nat_set.h"
|
||||||
#include "ast/ast_trail.h"
|
#include "ast/ast_trail.h"
|
||||||
#include "ast/arith_decl_plugin.h"
|
#include "ast/arith_decl_plugin.h"
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@ Author:
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "util/inf_rational.h"
|
#include "util/inf_rational.h"
|
||||||
#include "util/optional.h"
|
#include <optional>
|
||||||
|
|
||||||
namespace lp_api {
|
namespace lp_api {
|
||||||
|
|
||||||
|
|
@ -89,7 +89,7 @@ namespace lp_api {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
typedef optional<inf_rational> opt_inf_rational;
|
typedef std::optional<inf_rational> opt_inf_rational;
|
||||||
|
|
||||||
|
|
||||||
struct stats {
|
struct stats {
|
||||||
|
|
|
||||||
|
|
@ -30,6 +30,7 @@ Notes:
|
||||||
#include "util/inf_rational.h"
|
#include "util/inf_rational.h"
|
||||||
#include "smt/diff_logic.h"
|
#include "smt/diff_logic.h"
|
||||||
#include "smt/spanning_tree.h"
|
#include "smt/spanning_tree.h"
|
||||||
|
#include <optional>
|
||||||
|
|
||||||
namespace smt {
|
namespace smt {
|
||||||
|
|
||||||
|
|
@ -152,7 +153,7 @@ namespace smt {
|
||||||
unsigned m_step;
|
unsigned m_step;
|
||||||
edge_id m_enter_id;
|
edge_id m_enter_id;
|
||||||
edge_id m_leave_id;
|
edge_id m_leave_id;
|
||||||
optional<numeral> m_delta;
|
std::optional<numeral> m_delta;
|
||||||
|
|
||||||
// Initialize the network with a feasible spanning tree
|
// Initialize the network with a feasible spanning tree
|
||||||
void initialize();
|
void initialize();
|
||||||
|
|
|
||||||
|
|
@ -237,7 +237,7 @@ namespace smt {
|
||||||
bool network_flow<Ext>::choose_leaving_edge() {
|
bool network_flow<Ext>::choose_leaving_edge() {
|
||||||
node src = m_graph.get_source(m_enter_id);
|
node src = m_graph.get_source(m_enter_id);
|
||||||
node tgt = m_graph.get_target(m_enter_id);
|
node tgt = m_graph.get_target(m_enter_id);
|
||||||
m_delta.set_invalid();
|
m_delta.reset();
|
||||||
edge_id leave_id = null_edge_id;
|
edge_id leave_id = null_edge_id;
|
||||||
svector<edge_id> path;
|
svector<edge_id> path;
|
||||||
bool_vector against;
|
bool_vector against;
|
||||||
|
|
|
||||||
|
|
@ -550,7 +550,7 @@ class lp_parse {
|
||||||
};
|
};
|
||||||
|
|
||||||
struct bound {
|
struct bound {
|
||||||
optional<rational> m_lo, m_hi;
|
std::optional<rational> m_lo, m_hi;
|
||||||
bool m_int;
|
bool m_int;
|
||||||
bound() : m_int(false) {}
|
bound() : m_int(false) {}
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -27,7 +27,7 @@
|
||||||
#include "math/polynomial/algebraic_numbers.h"
|
#include "math/polynomial/algebraic_numbers.h"
|
||||||
#include "math/polynomial/polynomial.h"
|
#include "math/polynomial/polynomial.h"
|
||||||
#include "util/nat_set.h"
|
#include "util/nat_set.h"
|
||||||
#include "util/optional.h"
|
#include <optional>
|
||||||
#include "util/inf_rational.h"
|
#include "util/inf_rational.h"
|
||||||
#include "util/cancel_eh.h"
|
#include "util/cancel_eh.h"
|
||||||
#include "util/scoped_timer.h"
|
#include "util/scoped_timer.h"
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,7 @@ Notes:
|
||||||
#include "ast/pb_decl_plugin.h"
|
#include "ast/pb_decl_plugin.h"
|
||||||
#include "ast/for_each_expr.h"
|
#include "ast/for_each_expr.h"
|
||||||
#include "ast/rewriter/expr_replacer.h"
|
#include "ast/rewriter/expr_replacer.h"
|
||||||
#include "util/optional.h"
|
#include <optional>
|
||||||
#include "tactic/arith/bv2int_rewriter.h"
|
#include "tactic/arith/bv2int_rewriter.h"
|
||||||
#include "tactic/arith/bv2real_rewriter.h"
|
#include "tactic/arith/bv2real_rewriter.h"
|
||||||
#include "ast/converters/generic_model_converter.h"
|
#include "ast/converters/generic_model_converter.h"
|
||||||
|
|
@ -209,7 +209,7 @@ class nla2bv_tactic : public tactic {
|
||||||
void add_int_var(app* n) {
|
void add_int_var(app* n) {
|
||||||
expr_ref s_bv(m_manager);
|
expr_ref s_bv(m_manager);
|
||||||
sort_ref bv_sort(m_manager);
|
sort_ref bv_sort(m_manager);
|
||||||
optional<numeral> low, up;
|
std::optional<numeral> low, up;
|
||||||
numeral tmp;
|
numeral tmp;
|
||||||
bool is_strict;
|
bool is_strict;
|
||||||
if (m_bounds.has_lower(n, tmp, is_strict)) {
|
if (m_bounds.has_lower(n, tmp, is_strict)) {
|
||||||
|
|
|
||||||
|
|
@ -156,7 +156,6 @@ int main(int argc, char ** argv) {
|
||||||
TST(inf_rational);
|
TST(inf_rational);
|
||||||
TST(ast);
|
TST(ast);
|
||||||
TST(optional);
|
TST(optional);
|
||||||
TST(optional_benchmark);
|
|
||||||
TST(bit_vector);
|
TST(bit_vector);
|
||||||
TST(fixed_bit_vector);
|
TST(fixed_bit_vector);
|
||||||
TST(tbv);
|
TST(tbv);
|
||||||
|
|
|
||||||
|
|
@ -19,12 +19,11 @@ Revision History:
|
||||||
#include "util/trace.h"
|
#include "util/trace.h"
|
||||||
#include "util/debug.h"
|
#include "util/debug.h"
|
||||||
#include "util/memory_manager.h"
|
#include "util/memory_manager.h"
|
||||||
#include "util/optional.h"
|
#include <optional>
|
||||||
|
|
||||||
static void tst1() {
|
static void tst1() {
|
||||||
optional<int> v;
|
std::optional<int> v;
|
||||||
ENSURE(!v);
|
ENSURE(!v.has_value());
|
||||||
ENSURE(v == false);
|
|
||||||
v = 10;
|
v = 10;
|
||||||
ENSURE(v);
|
ENSURE(v);
|
||||||
ENSURE(*v == 10);
|
ENSURE(*v == 10);
|
||||||
|
|
@ -45,7 +44,7 @@ struct OptFoo {
|
||||||
};
|
};
|
||||||
|
|
||||||
static void tst2() {
|
static void tst2() {
|
||||||
optional<OptFoo> v;
|
std::optional<OptFoo> v;
|
||||||
ENSURE(!v);
|
ENSURE(!v);
|
||||||
v = OptFoo(10, 20);
|
v = OptFoo(10, 20);
|
||||||
ENSURE(v->m_x == 10);
|
ENSURE(v->m_x == 10);
|
||||||
|
|
@ -57,7 +56,7 @@ static void tst2() {
|
||||||
}
|
}
|
||||||
|
|
||||||
static void tst3() {
|
static void tst3() {
|
||||||
optional<int *> v;
|
std::optional<int *> v;
|
||||||
ENSURE(!v);
|
ENSURE(!v);
|
||||||
int x = 10;
|
int x = 10;
|
||||||
v = &x;
|
v = &x;
|
||||||
|
|
|
||||||
|
|
@ -1,404 +0,0 @@
|
||||||
/*++
|
|
||||||
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
|
|
||||||
// Prevent compiler optimization (portable for GCC/Clang and MSVC)
|
|
||||||
#if defined(_MSC_VER)
|
|
||||||
#include <intrin.h>
|
|
||||||
template<typename T>
|
|
||||||
inline void do_not_optimize(T const& value) {
|
|
||||||
// Trick MSVC into thinking value is used
|
|
||||||
volatile const T* volatile ptr = &value;
|
|
||||||
(void)ptr;
|
|
||||||
_ReadWriteBarrier();
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
template<typename T>
|
|
||||||
inline void do_not_optimize(T const& value) {
|
|
||||||
asm volatile("" : : "m"(value) : "memory");
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
@ -19,7 +19,7 @@ Revision History:
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "util/vector.h"
|
#include "util/vector.h"
|
||||||
#include "util/optional.h"
|
#include <optional>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
\brief Implements a mapping from Key to Data.
|
\brief Implements a mapping from Key to Data.
|
||||||
|
|
@ -43,29 +43,30 @@ class array_map {
|
||||||
unsigned m_garbage = 0;
|
unsigned m_garbage = 0;
|
||||||
unsigned m_non_garbage = 0;
|
unsigned m_non_garbage = 0;
|
||||||
static const unsigned m_gc_threshold = 10000;
|
static const unsigned m_gc_threshold = 10000;
|
||||||
vector<optional<entry>, CallDestructors > m_map;
|
vector<std::optional<entry>, CallDestructors > m_map;
|
||||||
Plugin m_plugin;
|
Plugin m_plugin;
|
||||||
|
|
||||||
bool is_current(optional<entry> const& e) const {
|
bool is_current(std::optional<entry> const& e) const {
|
||||||
return e->m_timestamp == m_timestamp;
|
return e->m_timestamp == m_timestamp;
|
||||||
}
|
}
|
||||||
|
|
||||||
optional<entry> const & get_core(Key const & k) const {
|
std::optional<entry> const & get_core(Key const & k) const {
|
||||||
unsigned id = m_plugin.to_int(k);
|
unsigned id = m_plugin.to_int(k);
|
||||||
if (id < m_map.size()) {
|
if (id < m_map.size()) {
|
||||||
optional<entry> const & e = m_map[id];
|
std::optional<entry> const & e = m_map[id];
|
||||||
if (e && is_current(e)) {
|
if (e && is_current(e)) {
|
||||||
return e;
|
return e;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return optional<entry>::undef();
|
static const std::optional<entry> s_undef;
|
||||||
|
return s_undef;
|
||||||
}
|
}
|
||||||
|
|
||||||
void really_flush() {
|
void really_flush() {
|
||||||
for (optional<entry> & e : m_map) {
|
for (std::optional<entry> & e : m_map) {
|
||||||
if (e) {
|
if (e) {
|
||||||
m_plugin.del_eh(e->m_key, e->m_data);
|
m_plugin.del_eh(e->m_key, e->m_data);
|
||||||
e.set_invalid();
|
e.reset();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
m_garbage = 0;
|
m_garbage = 0;
|
||||||
|
|
@ -81,11 +82,11 @@ public:
|
||||||
~array_map() { really_flush(); }
|
~array_map() { really_flush(); }
|
||||||
|
|
||||||
bool contains(Key const & k) const {
|
bool contains(Key const & k) const {
|
||||||
return get_core(k);
|
return get_core(k).has_value();
|
||||||
}
|
}
|
||||||
|
|
||||||
Data const & get(Key const & k) const {
|
Data const & get(Key const & k) const {
|
||||||
optional<entry> const & e = get_core(k);
|
std::optional<entry> const & e = get_core(k);
|
||||||
SASSERT(e);
|
SASSERT(e);
|
||||||
return e->m_data;
|
return e->m_data;
|
||||||
}
|
}
|
||||||
|
|
@ -103,11 +104,11 @@ public:
|
||||||
void insert(Key const & k, Data const & d) {
|
void insert(Key const & k, Data const & d) {
|
||||||
unsigned id = m_plugin.to_int(k);
|
unsigned id = m_plugin.to_int(k);
|
||||||
if (id >= m_map.size()) {
|
if (id >= m_map.size()) {
|
||||||
m_map.resize(id + 1, optional<entry>::undef());
|
m_map.resize(id + 1, std::nullopt);
|
||||||
}
|
}
|
||||||
|
|
||||||
m_plugin.ins_eh(k, d);
|
m_plugin.ins_eh(k, d);
|
||||||
optional<entry> & e = m_map[id];
|
std::optional<entry> & e = m_map[id];
|
||||||
if (e) {
|
if (e) {
|
||||||
if (!is_current(e)) {
|
if (!is_current(e)) {
|
||||||
--m_garbage;
|
--m_garbage;
|
||||||
|
|
@ -124,7 +125,7 @@ public:
|
||||||
void erase(Key const & k) {
|
void erase(Key const & k) {
|
||||||
unsigned id = m_plugin.to_int(k);
|
unsigned id = m_plugin.to_int(k);
|
||||||
if (id < m_map.size()) {
|
if (id < m_map.size()) {
|
||||||
optional<entry> & e = m_map[id];
|
std::optional<entry> & e = m_map[id];
|
||||||
if (e) {
|
if (e) {
|
||||||
m_plugin.del_eh(e->m_key, e->m_data);
|
m_plugin.del_eh(e->m_key, e->m_data);
|
||||||
if (is_current(e)) {
|
if (is_current(e)) {
|
||||||
|
|
@ -135,7 +136,7 @@ public:
|
||||||
SASSERT(m_garbage > 0);
|
SASSERT(m_garbage > 0);
|
||||||
--m_garbage;
|
--m_garbage;
|
||||||
}
|
}
|
||||||
e.set_invalid();
|
e.reset();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,154 +0,0 @@
|
||||||
/*++
|
|
||||||
Copyright (c) 2006 Microsoft Corporation
|
|
||||||
|
|
||||||
Module Name:
|
|
||||||
|
|
||||||
optional.h
|
|
||||||
|
|
||||||
Abstract:
|
|
||||||
|
|
||||||
Discriminated union of a type T.
|
|
||||||
It defines the notion of initialized/uninitialized objects.
|
|
||||||
|
|
||||||
Author:
|
|
||||||
|
|
||||||
Leonardo de Moura (leonardo) 2006-09-29.
|
|
||||||
|
|
||||||
Revision History:
|
|
||||||
|
|
||||||
--*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
template<class T>
|
|
||||||
class optional {
|
|
||||||
T* m_obj = nullptr;
|
|
||||||
|
|
||||||
void destroy() {
|
|
||||||
dealloc(m_obj);
|
|
||||||
m_obj = nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
public:
|
|
||||||
optional() = default;
|
|
||||||
|
|
||||||
explicit optional(const T & val) {
|
|
||||||
m_obj = alloc(T, val);
|
|
||||||
}
|
|
||||||
|
|
||||||
explicit optional(T && val) {
|
|
||||||
m_obj = alloc(T, std::move(val));
|
|
||||||
}
|
|
||||||
|
|
||||||
optional(optional<T> && val) noexcept {
|
|
||||||
std::swap(m_obj, val.m_obj);
|
|
||||||
}
|
|
||||||
|
|
||||||
optional(const optional<T> & val) {
|
|
||||||
if (val.m_obj) {
|
|
||||||
m_obj = alloc(T, *val);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
~optional() {
|
|
||||||
destroy();
|
|
||||||
}
|
|
||||||
|
|
||||||
static optional const & undef() { static optional u; return u; }
|
|
||||||
|
|
||||||
bool initialized() const { return m_obj; }
|
|
||||||
operator bool() const { return m_obj; }
|
|
||||||
bool operator!() const { return !m_obj; }
|
|
||||||
|
|
||||||
T * get() const {
|
|
||||||
return m_obj;
|
|
||||||
}
|
|
||||||
|
|
||||||
void set_invalid() {
|
|
||||||
destroy();
|
|
||||||
}
|
|
||||||
|
|
||||||
T * operator->() {
|
|
||||||
SASSERT(m_obj);
|
|
||||||
return m_obj;
|
|
||||||
}
|
|
||||||
|
|
||||||
T const * operator->() const {
|
|
||||||
SASSERT(m_obj);
|
|
||||||
return m_obj;
|
|
||||||
}
|
|
||||||
|
|
||||||
const T & operator*() const {
|
|
||||||
SASSERT(m_obj);
|
|
||||||
return *m_obj;
|
|
||||||
}
|
|
||||||
|
|
||||||
T & operator*() {
|
|
||||||
SASSERT(m_obj);
|
|
||||||
return *m_obj;
|
|
||||||
}
|
|
||||||
|
|
||||||
optional & operator=(const T & val) {
|
|
||||||
destroy();
|
|
||||||
m_obj = alloc(T, val);
|
|
||||||
return * this;
|
|
||||||
}
|
|
||||||
|
|
||||||
optional & operator=(optional && val) noexcept {
|
|
||||||
std::swap(m_obj, val.m_obj);
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
optional & operator=(const optional & val) {
|
|
||||||
if (&val != this) {
|
|
||||||
destroy();
|
|
||||||
if (val.m_obj) {
|
|
||||||
m_obj = alloc(T, *val);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
\brief Template specialization for pointers. NULL represents uninitialized pointers.
|
|
||||||
*/
|
|
||||||
template<typename T>
|
|
||||||
class optional<T*> {
|
|
||||||
T * m_ptr = nullptr;
|
|
||||||
|
|
||||||
static optional m_undef;
|
|
||||||
|
|
||||||
public:
|
|
||||||
|
|
||||||
optional() = default;
|
|
||||||
|
|
||||||
explicit optional(T * val):m_ptr(val) {}
|
|
||||||
|
|
||||||
static optional const & undef() { return m_undef; }
|
|
||||||
|
|
||||||
bool initialized() const { return m_ptr != 0 ; }
|
|
||||||
|
|
||||||
operator bool() const { return m_ptr != 0; }
|
|
||||||
|
|
||||||
bool operator!() const { return m_ptr == nullptr; }
|
|
||||||
|
|
||||||
void reset() { m_ptr = 0; }
|
|
||||||
|
|
||||||
optional & operator=(T * val) {
|
|
||||||
m_ptr = val;
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
optional & operator=(const optional & val) {
|
|
||||||
m_ptr = val.m_ptr;
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
T ** operator->() { return &m_ptr; }
|
|
||||||
|
|
||||||
T * operator*() const { return m_ptr; }
|
|
||||||
|
|
||||||
T * & operator*() { return m_ptr; }
|
|
||||||
};
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue