AimRT/_deps/libunifex-src/examples/linux/io_epoll_test.cpp
2025-01-12 20:42:42 +08:00

208 lines
6.9 KiB
C++

/*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* Licensed under the Apache License Version 2.0 with LLVM Exceptions
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* https://llvm.org/LICENSE.txt
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// writes starting!
// warmup completed!
// benchmark completed!
// completed in 10019 ms, 10019047256ns, 15058560ops
// stats - 1.50299e+06reads, 665ns-per-op, 1503ops-per-ms
// writes stopped!
#include <unifex/config.hpp>
#if !UNIFEX_NO_EPOLL
#include <unifex/inplace_stop_token.hpp>
#include <unifex/just.hpp>
#include <unifex/linux/io_epoll_context.hpp>
#include <unifex/scheduler_concepts.hpp>
#include <unifex/scope_guard.hpp>
#include <unifex/sequence.hpp>
#include <unifex/sync_wait.hpp>
#include <unifex/then.hpp>
#include <unifex/when_all.hpp>
#include <unifex/repeat_effect_until.hpp>
#include <unifex/typed_via.hpp>
#include <unifex/with_query_value.hpp>
#include <unifex/let_done.hpp>
#include <unifex/stop_when.hpp>
#include <unifex/defer.hpp>
#include <unifex/just_from.hpp>
#include <iostream>
#include <chrono>
#include <cstdio>
#include <string>
#include <thread>
#include <vector>
using namespace unifex;
using namespace unifex::linuxos;
using namespace std::chrono_literals;
inline constexpr auto sink = [](auto&&...){};
inline constexpr auto discard = then(sink);
//! Seconds to warmup the benchmark
static constexpr int WARMUP_DURATION = 3;
//! Seconds to run the benchmark
static constexpr int BENCHMARK_DURATION = 10;
static constexpr unsigned char data[6] = {'h', 'e', 'l', 'l', 'o', '\n'};
int main() {
io_epoll_context ctx;
inplace_stop_source stopSource;
std::thread t{[&] { ctx.run(stopSource.get_token()); }};
scope_guard stopOnExit = [&]() noexcept {
stopSource.request_stop();
t.join();
};
auto scheduler = ctx.get_scheduler();
try {
{
auto startTime = std::chrono::steady_clock::now();
inplace_stop_source timerStopSource;
auto task = when_all(
schedule_at(scheduler, now(scheduler) + 1s)
| then([] { std::printf("timer 1 completed (1s)\n"); }),
schedule_at(scheduler, now(scheduler) + 2s)
| then([] { std::printf("timer 2 completed (2s)\n"); }))
| stop_when(
schedule_at(scheduler, now(scheduler) + 1500ms)
| then([] { std::printf("timer 3 completed (1.5s) cancelling\n"); }));
sync_wait(std::move(task));
auto endTime = std::chrono::steady_clock::now();
std::printf(
"completed in %i ms\n",
(int)std::chrono::duration_cast<std::chrono::milliseconds>(
endTime - startTime)
.count());
}
} catch (const std::exception& ex) {
std::printf("error: %s\n", ex.what());
}
auto pipe_bench = [](auto& rPipeRef, auto& buffer, auto scheduler, int seconds,
[[maybe_unused]] auto& data, auto& reps, [[maybe_unused]] auto& offset) {
return defer([&, scheduler, seconds] {
return defer([&] {
return
// do read:
async_read_some(rPipeRef, as_writable_bytes(span{buffer.data() + 0, 1}))
| discard
| then([&] {
UNIFEX_ASSERT(data[(reps + offset) % sizeof(data)] == buffer[0]);
++reps;
});
})
| typed_via(scheduler)
// Repeat the reads:
| repeat_effect()
// stop reads after requested time
| stop_when(schedule_at(scheduler, now(scheduler) + std::chrono::seconds(seconds)))
// complete with void when requested time expires
| let_done([]{return just();});
});
};
auto pipe_write = [](auto& wPipeRef, auto databuffer, auto scheduler, auto stopToken) {
return
// write the data to one end of the pipe
sequence(
just_from([&]{ printf("writes starting!\n"); }),
defer([&, databuffer] { return discard(async_write_some(wPipeRef, databuffer)); })
| typed_via(scheduler)
| repeat_effect()
| let_done([]{return just();})
| with_query_value(get_stop_token, stopToken),
just_from([&]{ printf("writes stopped!\n"); }));
};
auto [rPipe, wPipe] = open_pipe(scheduler);
auto startTime = std::chrono::high_resolution_clock::now();
auto endTime = std::chrono::high_resolution_clock::now();
auto reps = 0;
auto offset = 0;
inplace_stop_source stopWrite;
const auto databuffer = as_bytes(span{data});
auto buffer = std::vector<char>{};
buffer.resize(1);
try {
auto task = when_all(
// write chunk of data into one end repeatedly
pipe_write(wPipe, databuffer, scheduler, stopWrite.get_token()),
// read the data 1 byte at a time from the other end
sequence(
// read for some time before starting measurement
// this is done to reduce startup effects
pipe_bench(rPipe, buffer, scheduler, WARMUP_DURATION, data, reps, offset),
// reset measurements to exclude warmup
just_from([&] {
// restart reps and keep offset in data
offset = reps%sizeof(data);
reps = 0;
printf("warmup completed!\n");
// exclude the warmup time
startTime = endTime = std::chrono::high_resolution_clock::now();
}),
// do more reads and measure how many reads occur
pipe_bench(rPipe, buffer, scheduler, BENCHMARK_DURATION, data, reps, offset),
// report results
just_from([&] {
endTime = std::chrono::high_resolution_clock::now();
printf("benchmark completed!\n");
auto ms = std::chrono::duration_cast<std::chrono::milliseconds>(
endTime - startTime)
.count();
auto ns = std::chrono::duration_cast<std::chrono::nanoseconds>(
endTime - startTime)
.count();
double reads = 1000000000.0 * reps / ns;
std::cout
<< "completed in "
<< ms << " ms, "
<< ns << "ns, "
<< reps << "ops\n";
std::cout
<< "stats - "
<< reads << "reads, "
<< ns/reps << "ns-per-op, "
<< reps/ms << "ops-per-ms\n";
stopWrite.request_stop();
})
)
);
sync_wait(std::move(task));
} catch (const std::system_error& se) {
std::printf("async_read_some system_error: [%s], [%s]\n", se.code().message().c_str(), se.what());
} catch (const std::exception& ex) {
std::printf("async_read_some exception: %s\n", ex.what());
}
return 0;
}
#else // !UNIFEX_NO_EPOLL
#include <cstdio>
int main() {
printf("epoll support not found\n");
}
#endif // !UNIFEX_NO_EPOLL