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