Skip to content
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
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
6 changes: 3 additions & 3 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ if(BOOST_COROSIO_IS_ROOT AND BUILD_SHARED_LIBS)
endif()

option(BOOST_COROSIO_BUILD_TESTS "Build boost::corosio tests" ${BUILD_TESTING})
option(BOOST_COROSIO_BUILD_PERF "Build boost::corosio performance tools" ${BOOST_COROSIO_IS_ROOT})
option(BOOST_COROSIO_BUILD_BENCH "Build boost::corosio benchmarks" ${BOOST_COROSIO_IS_ROOT})
option(BOOST_COROSIO_BUILD_EXAMPLES "Build boost::corosio examples" ${BOOST_COROSIO_IS_ROOT})
option(BOOST_COROSIO_MRDOCS_BUILD "Building for MrDocs documentation generation" OFF)

Expand Down Expand Up @@ -124,6 +124,6 @@ if (BOOST_COROSIO_BUILD_EXAMPLES)
add_subdirectory(example)
endif ()

if (BOOST_COROSIO_BUILD_PERF)
add_subdirectory(perf)
if (BOOST_COROSIO_BUILD_BENCH)
add_subdirectory(bench)
endif ()
15 changes: 14 additions & 1 deletion perf/bench/CMakeLists.txt → bench/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,19 @@
# Official repository: https://github.com/cppalliance/corosio
#

# Find Boost.Asio for comparison benchmarks (sibling or system-installed).
# This lives here (not in the root CMakeLists.txt) because the Boost
# superproject's dependency scanner greps Boost::* from the root file
# and would pull in Asio's full transitive dependency tree.
if(NOT TARGET Boost::asio)
find_package(Boost 1.84 QUIET COMPONENTS asio)
if(TARGET Boost::asio)
message(STATUS "Found system Boost.Asio -- comparison benchmarks enabled")
else()
message(STATUS "Boost.Asio not found -- comparison benchmarks disabled")
endif()
endif()

# Check LTO support for benchmarks
# MinGW GCC LTO mishandles virtual thunks from multiple inheritance,
# discarding COMDAT sections that contain needed thunk relocations.
Expand Down Expand Up @@ -40,7 +53,7 @@ target_link_libraries(corosio_bench
target_compile_options(corosio_bench PRIVATE
$<$<CXX_COMPILER_ID:MSVC>:/EHsc>)

set_property(TARGET corosio_bench PROPERTY FOLDER "perf/benchmarks")
set_property(TARGET corosio_bench PROPERTY FOLDER "bench")

if (COROSIO_BENCH_LTO_SUPPORTED)
set_property(TARGET corosio_bench PROPERTY INTERPROCEDURAL_OPTIMIZATION TRUE)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,35 +37,37 @@ namespace {
// to avoid TIME_WAIT accumulation. Reducing SO_SNDBUF/SO_RCVBUF from
// the macOS default of 128 KB each prevents ENOBUFS during rapid
// socket creation in concurrent/burst workloads.
static void configure_churn_socket( tcp_socket& s )
static void
configure_churn_socket(tcp_socket& s)
{
s.set_option( asio::socket_base::send_buffer_size( 1024 ) );
s.set_option( asio::socket_base::receive_buffer_size( 1024 ) );
s.set_option( asio::socket_base::linger( true, 0 ) );
s.set_option(asio::socket_base::send_buffer_size(1024));
s.set_option(asio::socket_base::receive_buffer_size(1024));
s.set_option(asio::socket_base::linger(true, 0));
}

// Creates a listening acceptor with retry. Under rapid socket churn the
// kernel may temporarily lack buffer space (ENOBUFS); a short back-off
// lets resources drain from the previous benchmark run.
static tcp_acceptor make_churn_acceptor( asio::io_context& ioc )
static tcp_acceptor
make_churn_acceptor(asio::io_context& ioc)
{
boost::system::error_code ec;
for( int attempt = 0; attempt < 20; ++attempt )
for (int attempt = 0; attempt < 20; ++attempt)
{
if( attempt > 0 )
std::this_thread::sleep_for( std::chrono::milliseconds( 50 ) );
tcp_acceptor acc( ioc.get_executor() );
ec = acc.open( tcp::v4(), ec );
if( !ec )
ec = acc.set_option( tcp_acceptor::reuse_address( true ), ec );
if( !ec )
ec = acc.bind( tcp::endpoint( tcp::v4(), 0 ), ec );
if( !ec )
ec = acc.listen( asio::socket_base::max_listen_connections, ec );
if( !ec )
if (attempt > 0)
std::this_thread::sleep_for(std::chrono::milliseconds(50));
tcp_acceptor acc(ioc.get_executor());
ec = acc.open(tcp::v4(), ec);
if (!ec)
ec = acc.set_option(tcp_acceptor::reuse_address(true), ec);
if (!ec)
ec = acc.bind(tcp::endpoint(tcp::v4(), 0), ec);
if (!ec)
ec = acc.listen(asio::socket_base::max_listen_connections, ec);
if (!ec)
return acc;
}
throw boost::system::system_error( ec );
throw boost::system::system_error(ec);
}

// Connect+accept+exchange 1 byte+close, repeat
Expand All @@ -92,18 +94,18 @@ struct sequential_churn_op

sw.reset();
connect_done = false;
accept_done = false;
client = tcp_socket( ioc.get_executor() );
server = tcp_socket( ioc.get_executor() );
accept_done = false;
client = tcp_socket(ioc.get_executor());
server = tcp_socket(ioc.get_executor());

boost::system::error_code ec;
ec = client.open( tcp::v4(), ec );
if( ec )
ec = client.open(tcp::v4(), ec);
if (ec)
{
asio::post( ioc, [this]() { start(); } );
asio::post(ioc, [this]() { start(); });
return;
}
configure_churn_socket( client );
configure_churn_socket(client);

client.async_connect(ep, [this](boost::system::error_code ec) {
if (ec)
Expand Down Expand Up @@ -163,22 +165,30 @@ void
bench_sequential_churn(bench::state& state)
{
asio::io_context ioc;
auto acc = make_churn_acceptor( ioc );
auto ep = tcp::endpoint( asio::ip::address_v4::loopback(), acc.local_endpoint().port() );
auto acc = make_churn_acceptor(ioc);
auto ep = tcp::endpoint(
asio::ip::address_v4::loopback(), acc.local_endpoint().port());

std::atomic<bool> running{true};

sequential_churn_op op{ioc, acc, ep, running, state.latency(),
state.ops(),
tcp_socket(ioc.get_executor()),
tcp_socket(ioc.get_executor()), {}};
sequential_churn_op op{
ioc,
acc,
ep,
running,
state.latency(),
state.ops(),
tcp_socket(ioc.get_executor()),
tcp_socket(ioc.get_executor()),
{}};

perf::stopwatch total_sw;

op.start();

std::thread timer([&]() {
std::this_thread::sleep_for(std::chrono::duration<double>(state.duration()));
std::this_thread::sleep_for(
std::chrono::duration<double>(state.duration()));
running.store(false, std::memory_order_relaxed);
ioc.stop();
});
Expand All @@ -194,22 +204,30 @@ void
bench_sequential_churn_lockless(bench::state& state)
{
asio::io_context ioc(BOOST_ASIO_CONCURRENCY_HINT_UNSAFE);
auto acc = make_churn_acceptor( ioc );
auto ep = tcp::endpoint( asio::ip::address_v4::loopback(), acc.local_endpoint().port() );
auto acc = make_churn_acceptor(ioc);
auto ep = tcp::endpoint(
asio::ip::address_v4::loopback(), acc.local_endpoint().port());

std::atomic<bool> running{true};

sequential_churn_op op{ioc, acc, ep, running, state.latency(),
state.ops(),
tcp_socket(ioc.get_executor()),
tcp_socket(ioc.get_executor()), {}};
sequential_churn_op op{
ioc,
acc,
ep,
running,
state.latency(),
state.ops(),
tcp_socket(ioc.get_executor()),
tcp_socket(ioc.get_executor()),
{}};

perf::stopwatch total_sw;

op.start();

std::thread timer([&]() {
std::this_thread::sleep_for(std::chrono::duration<double>(state.duration()));
std::this_thread::sleep_for(
std::chrono::duration<double>(state.duration()));
running.store(false, std::memory_order_relaxed);
ioc.stop();
});
Expand All @@ -226,16 +244,16 @@ bench_sequential_churn_lockless(bench::state& state)
void
bench_concurrent_churn(bench::state& state)
{
int num_loops = static_cast<int>(state.range(0));
int num_loops = static_cast<int>(state.range(0));
state.counters["num_loops"] = num_loops;

asio::io_context ioc;
std::atomic<bool> running{true};

std::vector<tcp_acceptor> acceptors;
acceptors.reserve( num_loops );
for( int i = 0; i < num_loops; ++i )
acceptors.push_back( make_churn_acceptor( ioc ) );
acceptors.reserve(num_loops);
for (int i = 0; i < num_loops; ++i)
acceptors.push_back(make_churn_acceptor(ioc));

std::vector<std::unique_ptr<sequential_churn_op>> ops;
ops.reserve(num_loops);
Expand All @@ -245,17 +263,25 @@ bench_concurrent_churn(bench::state& state)
for (int i = 0; i < num_loops; ++i)
{
auto ep = tcp::endpoint(
asio::ip::address_v4::loopback(), acceptors[i].local_endpoint().port() );
ops.push_back( std::make_unique<sequential_churn_op>(
sequential_churn_op{ ioc, acceptors[i], ep, running,
state.latency(), state.ops(),
tcp_socket(ioc.get_executor()),
tcp_socket(ioc.get_executor()), {} } ) );
asio::ip::address_v4::loopback(),
acceptors[i].local_endpoint().port());
ops.push_back(
std::make_unique<sequential_churn_op>(sequential_churn_op{
ioc,
acceptors[i],
ep,
running,
state.latency(),
state.ops(),
tcp_socket(ioc.get_executor()),
tcp_socket(ioc.get_executor()),
{}}));
ops.back()->start();
}

std::thread stopper([&]() {
std::this_thread::sleep_for(std::chrono::duration<double>(state.duration()));
std::this_thread::sleep_for(
std::chrono::duration<double>(state.duration()));
running.store(false, std::memory_order_relaxed);
ioc.stop();
});
Expand All @@ -264,7 +290,7 @@ bench_concurrent_churn(bench::state& state)
stopper.join();

state.set_elapsed(total_sw.elapsed_seconds());
for( auto& a : acceptors )
for (auto& a : acceptors)
a.close();
}

Expand Down Expand Up @@ -299,27 +325,26 @@ struct burst_churn_op

// Open all client sockets before issuing async operations so a
// partial failure doesn't leave dangling async_accept operations.
for( int i = 0; i < burst_size; ++i )
for (int i = 0; i < burst_size; ++i)
{
clients.emplace_back( ioc.get_executor() );
clients.emplace_back(ioc.get_executor());
boost::system::error_code ec;
ec = clients.back().open( tcp::v4(), ec );
if( ec )
ec = clients.back().open(tcp::v4(), ec);
if (ec)
{
clients.clear();
asio::post( ioc, [this]() { start(); } );
asio::post(ioc, [this]() { start(); });
return;
}
configure_churn_socket( clients.back() );
configure_churn_socket(clients.back());
}

// Initiate all connects and accepts
for( int i = 0; i < burst_size; ++i )
for (int i = 0; i < burst_size; ++i)
{
clients[i].async_connect( ep,
[](boost::system::error_code) {} );
clients[i].async_connect(ep, [](boost::system::error_code) {});

servers.emplace_back( ioc.get_executor() );
servers.emplace_back(ioc.get_executor());
acc.async_accept(
servers.back(), [this](boost::system::error_code ec) {
if (ec)
Expand Down Expand Up @@ -350,25 +375,27 @@ struct burst_churn_op
void
bench_burst_churn(bench::state& state)
{
int burst_size = static_cast<int>(state.range(0));
int burst_size = static_cast<int>(state.range(0));
state.counters["burst_size"] = burst_size;

asio::io_context ioc;
auto acc = make_churn_acceptor( ioc );
auto ep = tcp::endpoint( asio::ip::address_v4::loopback(), acc.local_endpoint().port() );
auto acc = make_churn_acceptor(ioc);
auto ep = tcp::endpoint(
asio::ip::address_v4::loopback(), acc.local_endpoint().port());

std::atomic<bool> running{true};

burst_churn_op op{ioc, acc, ep, running, state.latency(),
state.ops(), burst_size, {}, {}, {},
burst_churn_op op{ioc, acc, ep, running, state.latency(),
state.ops(), burst_size, {}, {}, {},
{}};

perf::stopwatch total_sw;

op.start();

std::thread stopper([&]() {
std::this_thread::sleep_for(std::chrono::duration<double>(state.duration()));
std::this_thread::sleep_for(
std::chrono::duration<double>(state.duration()));
running.store(false, std::memory_order_relaxed);
ioc.stop();
});
Expand All @@ -383,25 +410,27 @@ bench_burst_churn(bench::state& state)
void
bench_burst_churn_lockless(bench::state& state)
{
int burst_size = static_cast<int>(state.range(0));
int burst_size = static_cast<int>(state.range(0));
state.counters["burst_size"] = burst_size;

asio::io_context ioc(BOOST_ASIO_CONCURRENCY_HINT_UNSAFE);
auto acc = make_churn_acceptor( ioc );
auto ep = tcp::endpoint( asio::ip::address_v4::loopback(), acc.local_endpoint().port() );
auto acc = make_churn_acceptor(ioc);
auto ep = tcp::endpoint(
asio::ip::address_v4::loopback(), acc.local_endpoint().port());

std::atomic<bool> running{true};

burst_churn_op op{ioc, acc, ep, running, state.latency(),
state.ops(), burst_size, {}, {}, {},
burst_churn_op op{ioc, acc, ep, running, state.latency(),
state.ops(), burst_size, {}, {}, {},
{}};

perf::stopwatch total_sw;

op.start();

std::thread stopper([&]() {
std::this_thread::sleep_for(std::chrono::duration<double>(state.duration()));
std::this_thread::sleep_for(
std::chrono::duration<double>(state.duration()));
running.store(false, std::memory_order_relaxed);
ioc.stop();
});
Expand All @@ -423,11 +452,11 @@ make_accept_churn_suite()
.add("sequential", bench_sequential_churn)
.add("sequential_lockless", bench_sequential_churn_lockless)
.add("concurrent", bench_concurrent_churn)
.args({1, 4, 16})
.args({1, 4, 16})
.add("burst", bench_burst_churn)
.args({10, 100})
.args({10, 100})
.add("burst_lockless", bench_burst_churn_lockless)
.args({10, 100});
.args({10, 100});
}

} // namespace asio_callback_bench
Loading
Loading