blob: c8d3797633082c4542eaa461d7abbd746eb1b81b [file] [log] [blame]
Robin Lee2cf56172016-09-13 18:55:42 +09001/*
2 * Copyright (C) 2016 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#define LOG_TAG "connect_benchmark"
18
Robin Lee711237c2016-11-07 19:55:00 +000019/*
20 * See README.md for general notes.
21 *
22 * This set of benchmarks measures the throughput of connect() calls on a single thread for IPv4 and
Bernie Innocenticd257642018-12-20 15:56:40 +090023 * IPv6.
Robin Lee711237c2016-11-07 19:55:00 +000024 *
25 * Realtime timed tests
26 * ====================
27 *
28 * The tests named *_high_load record the following useful information:
29 *
30 * - real_time: the mean roundtrip time for one connect() call under load
31 *
32 * - iterations: the number of times the test was run within the timelimit --- approximately
33 * MinTime / real_time
34 *
35 * Manually timed tests
36 * ====================
37 *
38 * All other sets of tests apart from *_high_load run with manual timing. The purpose of these is to
39 * measure 90th-percentile latency for connect() calls compared to mean latency.
40 *
41 * (TODO: ideally this should be against median latency, but google-benchmark only supports one
42 * custom 'label' output for graphing. Stddev isn't appropriate because the latency
43 * distribution is usually spiky, not in a nice neat normal-like distribution.)
44 *
45 * The manually timed tests record the following useful information:
46 *
47 * - real_time: the average time taken to complete a test run. Unlike the real_time used in high
48 * load tests, this is calculated from before-and-after values of the realtime clock
49 * over many iterations so may be less accurate than the under-load times.
50 *
51 * - iterations: the number of times the test was run within the timelimit --- approximately
52 * MinTime / real_time, although as explained, may not be as meaningful because of
53 * overhead from timing.
54 *
55 * - label: a manually-recorded time giving the 90th-percentile value of real_time over all
56 * individual runs. Should be compared to real_time.
57 *
58 */
59
Robin Lee2cf56172016-09-13 18:55:42 +090060#include <arpa/inet.h>
61#include <cutils/sockets.h>
62#include <errno.h>
63#include <netinet/in.h>
64#include <time.h>
65
66#include <map>
67#include <functional>
68#include <thread>
69
70#include <android-base/stringprintf.h>
71#include <benchmark/benchmark.h>
72#include <log/log.h>
73#include <utils/StrongPointer.h>
74
75#include "FwmarkClient.h"
76#include "SockDiag.h"
77#include "Stopwatch.h"
78
79using android::base::StringPrintf;
Robin Lee2cf56172016-09-13 18:55:42 +090080
81static int bindAndListen(int s) {
82 sockaddr_in6 sin6 = { .sin6_family = AF_INET6 };
83 if (bind(s, (sockaddr*) &sin6, sizeof(sin6)) == 0) {
84 if (listen(s, 1)) {
85 return -1;
86 }
87 sockaddr_in sin = {};
88 socklen_t len = sizeof(sin);
89 if (getsockname(s, (sockaddr*) &sin, &len)) {
90 return -1;
91 }
92 return ntohs(sin.sin_port);
93 } else {
94 return -1;
95 }
96}
97
98static void ipv4_loopback(benchmark::State& state, const bool waitBetweenRuns) {
Erik Klineab999f12018-07-04 11:29:31 +090099 const int listensocket = socket(AF_INET6, SOCK_STREAM | SOCK_CLOEXEC, 0);
Robin Lee2cf56172016-09-13 18:55:42 +0900100 const int port = bindAndListen(listensocket);
101 if (port == -1) {
102 state.SkipWithError("Unable to bind server socket");
103 return;
104 }
105
106 // ALOGW("Listening on port = %d", port);
107 std::vector<uint64_t> latencies(state.max_iterations);
108 uint64_t iterations = 0;
109
110 while (state.KeepRunning()) {
Erik Klineab999f12018-07-04 11:29:31 +0900111 int sock = socket(AF_INET, SOCK_STREAM | SOCK_CLOEXEC, 0);
Robin Lee2cf56172016-09-13 18:55:42 +0900112 if (sock < 0) {
113 state.SkipWithError(StringPrintf("socket() failed with errno=%d", errno).c_str());
114 break;
115 }
116
117 const Stopwatch stopwatch;
118
119 sockaddr_in server = { .sin_family = AF_INET, .sin_port = htons(port) };
Robin Leee65244b2016-12-01 19:03:33 +0000120 if (connect(sock, (sockaddr*) &server, sizeof(server))) {
Robin Lee2cf56172016-09-13 18:55:42 +0900121 state.SkipWithError(StringPrintf("connect() failed with errno=%d", errno).c_str());
122 close(sock);
123 break;
124 }
125
126 if (waitBetweenRuns) {
127 latencies[iterations] = stopwatch.timeTaken() * 1e6L;
128 state.SetIterationTime(latencies[iterations] / 1e9L);
129 std::this_thread::sleep_for(std::chrono::milliseconds(10));
130 ++iterations;
131 }
132
133 sockaddr_in6 client;
134 socklen_t clientlen = sizeof(client);
Erik Klineab999f12018-07-04 11:29:31 +0900135 int accepted = accept4(listensocket, (sockaddr*) &client, &clientlen, SOCK_CLOEXEC);
Robin Lee2cf56172016-09-13 18:55:42 +0900136 if (accepted < 0) {
137 state.SkipWithError(StringPrintf("accept() failed with errno=%d", errno).c_str());
138 close(sock);
139 break;
140 }
141
142 close(accepted);
143 close(sock);
144 }
145 close(listensocket);
146 // ALOGI("Finished test on port = %d", port);
147
148 if (iterations > 0) {
149 latencies.resize(iterations);
150 sort(latencies.begin(), latencies.end());
Chih-hung Hsieh03d6f382017-06-21 20:27:48 +0000151 state.SetLabel(StringPrintf("%lld", (long long) latencies[iterations * 9 / 10]));
Robin Lee2cf56172016-09-13 18:55:42 +0900152 }
153}
154
155static void ipv6_loopback(benchmark::State& state, const bool waitBetweenRuns) {
Erik Klineab999f12018-07-04 11:29:31 +0900156 const int listensocket = socket(AF_INET6, SOCK_STREAM | SOCK_CLOEXEC, 0);
Robin Lee2cf56172016-09-13 18:55:42 +0900157 const int port = bindAndListen(listensocket);
158 if (port == -1) {
159 state.SkipWithError("Unable to bind server socket");
160 return;
161 }
162
163 // ALOGW("Listening on port = %d", port);
164 std::vector<uint64_t> latencies(state.max_iterations);
165 uint64_t iterations = 0;
166
167 while (state.KeepRunning()) {
Erik Klineab999f12018-07-04 11:29:31 +0900168 int sock = socket(AF_INET6, SOCK_STREAM | SOCK_CLOEXEC, 0);
Robin Lee2cf56172016-09-13 18:55:42 +0900169 if (sock < 0) {
170 state.SkipWithError(StringPrintf("socket() failed with errno=%d", errno).c_str());
171 break;
172 }
173
174 const Stopwatch stopwatch;
175
176 sockaddr_in6 server = { .sin6_family = AF_INET6, .sin6_port = htons(port) };
Robin Leee65244b2016-12-01 19:03:33 +0000177 if (connect(sock, (sockaddr*) &server, sizeof(server))) {
Robin Lee2cf56172016-09-13 18:55:42 +0900178 state.SkipWithError(StringPrintf("connect() failed with errno=%d", errno).c_str());
179 close(sock);
180 break;
181 }
182
183 if (waitBetweenRuns) {
184 latencies[iterations] = stopwatch.timeTaken() * 1e6L;
185 state.SetIterationTime(latencies[iterations] / 1e9L);
186 std::this_thread::sleep_for(std::chrono::milliseconds(10));
187 ++iterations;
188 }
189
190 sockaddr_in6 client;
191 socklen_t clientlen = sizeof(client);
Erik Klineab999f12018-07-04 11:29:31 +0900192 int accepted = accept4(listensocket, (sockaddr*) &client, &clientlen, SOCK_CLOEXEC);
Robin Lee2cf56172016-09-13 18:55:42 +0900193 if (accepted < 0) {
194 state.SkipWithError(StringPrintf("accept() failed with errno=%d", errno).c_str());
195 close(sock);
196 break;
197 }
198
199 close(accepted);
200 close(sock);
201 }
202 close(listensocket);
203 // ALOGI("Finished test on port = %d", port);
204
205 if (iterations > 0) {
206 latencies.resize(iterations);
207 sort(latencies.begin(), latencies.end());
Chih-hung Hsieh03d6f382017-06-21 20:27:48 +0000208 state.SetLabel(StringPrintf("%lld", (long long) latencies[iterations * 9 / 10]));
Robin Lee2cf56172016-09-13 18:55:42 +0900209 }
210}
211
Bernie Innocenticd257642018-12-20 15:56:40 +0900212static void run(decltype(ipv4_loopback) benchmarkFunction, ::benchmark::State& state,
213 const bool waitBetweenRuns) {
Robin Lee2cf56172016-09-13 18:55:42 +0900214 benchmarkFunction(state, waitBetweenRuns);
Robin Lee2cf56172016-09-13 18:55:42 +0900215}
216
217constexpr int MIN_THREADS = 1;
218constexpr int MAX_THREADS = 1;
219constexpr double MIN_TIME = 0.5 /* seconds */;
220
Bernie Innocenticd257642018-12-20 15:56:40 +0900221// IPv4 benchmarks under no load
222static void ipv4_no_load(::benchmark::State& state) {
223 run(ipv4_loopback, state, true);
Robin Lee2cf56172016-09-13 18:55:42 +0900224}
Bernie Innocenticd257642018-12-20 15:56:40 +0900225BENCHMARK(ipv4_no_load)->MinTime(MIN_TIME)->UseManualTime();
Robin Lee2cf56172016-09-13 18:55:42 +0900226
227// IPv4 benchmarks under high load
Bernie Innocenticd257642018-12-20 15:56:40 +0900228static void ipv4_high_load(::benchmark::State& state) {
229 run(ipv4_loopback, state, false);
Robin Lee2cf56172016-09-13 18:55:42 +0900230}
Bernie Innocenticd257642018-12-20 15:56:40 +0900231BENCHMARK(ipv4_high_load)->ThreadRange(MIN_THREADS, MAX_THREADS)->MinTime(MIN_TIME)->UseRealTime();
Robin Lee2cf56172016-09-13 18:55:42 +0900232
233// IPv6 raw connect() without using fwmark
Bernie Innocenticd257642018-12-20 15:56:40 +0900234static void ipv6_no_load(::benchmark::State& state) {
235 run(ipv6_loopback, state, true);
Robin Lee2cf56172016-09-13 18:55:42 +0900236}
Bernie Innocenticd257642018-12-20 15:56:40 +0900237BENCHMARK(ipv6_no_load)->MinTime(MIN_TIME)->UseManualTime();
Robin Lee2cf56172016-09-13 18:55:42 +0900238
239// IPv6 benchmarks under high load
Bernie Innocenticd257642018-12-20 15:56:40 +0900240static void ipv6_high_load(::benchmark::State& state) {
241 run(ipv6_loopback, state, false);
Robin Lee2cf56172016-09-13 18:55:42 +0900242}
Bernie Innocenticd257642018-12-20 15:56:40 +0900243BENCHMARK(ipv6_high_load)->ThreadRange(MIN_THREADS, MAX_THREADS)->MinTime(MIN_TIME)->UseRealTime();