blob: 0507f26e25d72a02fb711764710146dea0099a85 [file] [log] [blame]
Austin Schuh5af45eb2019-09-16 20:54:18 -07001#include <sys/eventfd.h>
2#include <sys/stat.h>
3#include <sys/types.h>
Austin Schuh60e77942022-05-16 17:48:24 -07004
Austin Schuh5af45eb2019-09-16 20:54:18 -07005#include <chrono>
6#include <random>
7#include <thread>
8
9#include "aos/events/epoll.h"
10#include "aos/init.h"
11#include "aos/ipc_lib/latency_lib.h"
12#include "aos/logging/implementations.h"
13#include "aos/logging/logging.h"
14#include "aos/realtime.h"
15#include "aos/time/time.h"
Austin Schuh60e77942022-05-16 17:48:24 -070016#include "gflags/gflags.h"
Austin Schuh5af45eb2019-09-16 20:54:18 -070017
18// This is a demo program which uses named pipes to communicate.
19// It measures both latency of a random timer thread, and latency of the
20// pipe.
21
22DEFINE_int32(seconds, 10, "Duration of the test to run");
23DEFINE_int32(
24 latency_threshold, 1000,
25 "Disable tracing when anything takes more than this many microseoncds");
26DEFINE_int32(core, 7, "Core to pin to");
27DEFINE_int32(sender_priority, 53, "RT priority to send at");
28DEFINE_int32(receiver_priority, 52, "RT priority to receive at");
29DEFINE_int32(timer_priority, 51, "RT priority to spin the timer at");
30
31DEFINE_bool(log_latency, false, "If true, log the latency");
32
33namespace chrono = ::std::chrono;
34
35namespace aos {
36
37void SenderThread(int fd) {
38 const monotonic_clock::time_point end_time =
39 monotonic_clock::now() + chrono::seconds(FLAGS_seconds);
40 // Standard mersenne_twister_engine seeded with 0
41 ::std::mt19937 generator(0);
42
43 // Sleep between 1 and 15 ms.
44 ::std::uniform_int_distribution<> distribution(1000, 15000);
45
Austin Schuh9014e3b2020-11-21 14:26:07 -080046 SetCurrentThreadAffinity(MakeCpusetFromCpus({FLAGS_core}));
Austin Schuh5af45eb2019-09-16 20:54:18 -070047 SetCurrentThreadRealtimePriority(FLAGS_sender_priority);
48 while (true) {
49 const monotonic_clock::time_point wakeup_time =
50 monotonic_clock::now() + chrono::microseconds(distribution(generator));
51
52 ::std::this_thread::sleep_until(wakeup_time);
53 const monotonic_clock::time_point monotonic_now = monotonic_clock::now();
54 char sent_time_buffer[8];
55 memcpy(sent_time_buffer, &monotonic_now, sizeof(sent_time_buffer));
56 PCHECK(write(fd, sent_time_buffer, sizeof(sent_time_buffer)));
57
58 if (monotonic_now > end_time) {
59 break;
60 }
61 }
62
63 {
64 ::std::this_thread::sleep_for(chrono::milliseconds(100));
65 const monotonic_clock::time_point stop_time(chrono::nanoseconds(1));
66 char sent_time_buffer[8];
67 memcpy(sent_time_buffer, &stop_time, sizeof(sent_time_buffer));
68 PCHECK(write(fd, sent_time_buffer, sizeof(sent_time_buffer)));
69 }
70 UnsetCurrentThreadRealtimePriority();
71}
72
73void ReceiverThread(int fd) {
74 Tracing t;
75 t.Start();
76
77 chrono::nanoseconds max_wakeup_latency = chrono::nanoseconds(0);
78
79 chrono::nanoseconds sum_latency = chrono::nanoseconds(0);
80 int latency_count = 0;
81
82 internal::EPoll epoll;
83
84 epoll.OnReadable(fd, [&t, &epoll, &max_wakeup_latency, &sum_latency,
85 &latency_count, fd]() {
86 char sent_time_buffer[8];
87 const int ret = read(fd, static_cast<void *>(sent_time_buffer),
88 sizeof(sent_time_buffer));
89 const monotonic_clock::time_point monotonic_now = monotonic_clock::now();
90 CHECK_EQ(ret, 8);
91
92 monotonic_clock::time_point sent_time;
93 memcpy(&sent_time, sent_time_buffer, sizeof(sent_time_buffer));
94
95 if (sent_time == monotonic_clock::time_point(chrono::nanoseconds(1))) {
96 epoll.Quit();
97 return;
98 }
99
100 const chrono::nanoseconds wakeup_latency = monotonic_now - sent_time;
101
102 sum_latency += wakeup_latency;
103 ++latency_count;
104
105 max_wakeup_latency = ::std::max(wakeup_latency, max_wakeup_latency);
106
107 if (wakeup_latency > chrono::microseconds(FLAGS_latency_threshold)) {
108 t.Stop();
109 AOS_LOG(INFO, "Stopped tracing, latency %" PRId64 "\n",
110 static_cast<int64_t>(wakeup_latency.count()));
111 }
112
113 if (FLAGS_log_latency) {
114 AOS_LOG(INFO, "dt: %8d.%03d\n",
115 static_cast<int>(wakeup_latency.count() / 1000),
116 static_cast<int>(wakeup_latency.count() % 1000));
117 }
118 });
119
Austin Schuh9014e3b2020-11-21 14:26:07 -0800120 SetCurrentThreadAffinity(MakeCpusetFromCpus({FLAGS_core}));
Austin Schuh5af45eb2019-09-16 20:54:18 -0700121 SetCurrentThreadRealtimePriority(FLAGS_receiver_priority);
122 epoll.Run();
123 UnsetCurrentThreadRealtimePriority();
124 epoll.DeleteFd(fd);
125
126 const chrono::nanoseconds average_latency = sum_latency / latency_count;
127
128 AOS_LOG(INFO,
129 "Max eventfd wakeup latency: %d.%03d microseconds, average: %d.%03d "
130 "microseconds\n",
131 static_cast<int>(max_wakeup_latency.count() / 1000),
132 static_cast<int>(max_wakeup_latency.count() % 1000),
133 static_cast<int>(average_latency.count() / 1000),
134 static_cast<int>(average_latency.count() % 1000));
135}
136
137int Main(int /*argc*/, char ** /*argv*/) {
138 AOS_LOG(INFO, "Main!\n");
139 ::std::thread t([]() {
140 TimerThread(monotonic_clock::now() + chrono::seconds(FLAGS_seconds),
141 FLAGS_timer_priority);
142 });
143
144 int fd = eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK);
145 PCHECK(fd);
146
147 ::std::thread st([&fd]() { SenderThread(fd); });
148
149 ReceiverThread(fd);
150 st.join();
151
152 PCHECK(close(fd));
153
154 t.join();
155 return 0;
156}
157
158} // namespace aos
159
160int main(int argc, char **argv) {
161 ::gflags::ParseCommandLineFlags(&argc, &argv, true);
162
Austin Schuh5af45eb2019-09-16 20:54:18 -0700163 return ::aos::Main(argc, argv);
164}