• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
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 
19 /*
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
23  * IPv6.
24  *
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 
60 #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 <netdutils/Stopwatch.h>
74 #include <utils/StrongPointer.h>
75 
76 #include "FwmarkClient.h"
77 #include "SockDiag.h"
78 
79 using android::base::StringPrintf;
80 using android::netdutils::Stopwatch;
81 
bindAndListen(int s)82 static int bindAndListen(int s) {
83     sockaddr_in6 sin6 = { .sin6_family = AF_INET6 };
84     if (bind(s, (sockaddr*) &sin6, sizeof(sin6)) == 0) {
85         if (listen(s, 1)) {
86             return -1;
87         }
88         sockaddr_in sin = {};
89         socklen_t len = sizeof(sin);
90         if (getsockname(s, (sockaddr*) &sin, &len)) {
91             return -1;
92         }
93         return ntohs(sin.sin_port);
94     } else {
95         return -1;
96     }
97 }
98 
ipv4_loopback(benchmark::State & state,const bool waitBetweenRuns)99 static void ipv4_loopback(benchmark::State& state, const bool waitBetweenRuns) {
100     const int listensocket = socket(AF_INET6, SOCK_STREAM | SOCK_CLOEXEC, 0);
101     const int port = bindAndListen(listensocket);
102     if (port == -1) {
103         state.SkipWithError("Unable to bind server socket");
104         return;
105     }
106 
107     // ALOGW("Listening on port = %d", port);
108     std::vector<uint64_t> latencies(state.max_iterations);
109     uint64_t iterations = 0;
110 
111     while (state.KeepRunning()) {
112         int sock = socket(AF_INET, SOCK_STREAM | SOCK_CLOEXEC, 0);
113         if (sock < 0) {
114             state.SkipWithError(StringPrintf("socket() failed with errno=%d", errno).c_str());
115             break;
116         }
117 
118         const Stopwatch stopwatch;
119 
120         sockaddr_in server = { .sin_family = AF_INET, .sin_port = htons(port) };
121         if (connect(sock, (sockaddr*) &server, sizeof(server))) {
122             state.SkipWithError(StringPrintf("connect() failed with errno=%d", errno).c_str());
123             close(sock);
124             break;
125         }
126 
127         if (waitBetweenRuns) {
128             latencies[iterations] = stopwatch.timeTakenUs();
129             state.SetIterationTime(static_cast<double>(latencies[iterations]) / 1.0e6L);
130             std::this_thread::sleep_for(std::chrono::milliseconds(10));
131             ++iterations;
132         }
133 
134         sockaddr_in6 client;
135         socklen_t clientlen = sizeof(client);
136         int accepted = accept4(listensocket, (sockaddr*) &client, &clientlen, SOCK_CLOEXEC);
137         if (accepted < 0) {
138             state.SkipWithError(StringPrintf("accept() failed with errno=%d", errno).c_str());
139             close(sock);
140             break;
141         }
142 
143         close(accepted);
144         close(sock);
145     }
146     close(listensocket);
147     // ALOGI("Finished test on port = %d", port);
148 
149     if (iterations > 0) {
150         latencies.resize(iterations);
151         sort(latencies.begin(), latencies.end());
152         state.SetLabel(StringPrintf("%lld", (long long) latencies[iterations * 9 / 10]));
153     }
154 }
155 
ipv6_loopback(benchmark::State & state,const bool waitBetweenRuns)156 static void ipv6_loopback(benchmark::State& state, const bool waitBetweenRuns) {
157     const int listensocket = socket(AF_INET6, SOCK_STREAM | SOCK_CLOEXEC, 0);
158     const int port = bindAndListen(listensocket);
159     if (port == -1) {
160         state.SkipWithError("Unable to bind server socket");
161         return;
162     }
163 
164     // ALOGW("Listening on port = %d", port);
165     std::vector<uint64_t> latencies(state.max_iterations);
166     uint64_t iterations = 0;
167 
168     while (state.KeepRunning()) {
169         int sock = socket(AF_INET6, SOCK_STREAM | SOCK_CLOEXEC, 0);
170         if (sock < 0) {
171             state.SkipWithError(StringPrintf("socket() failed with errno=%d", errno).c_str());
172             break;
173         }
174 
175         const Stopwatch stopwatch;
176 
177         sockaddr_in6 server = { .sin6_family = AF_INET6, .sin6_port = htons(port) };
178         if (connect(sock, (sockaddr*) &server, sizeof(server))) {
179             state.SkipWithError(StringPrintf("connect() failed with errno=%d", errno).c_str());
180             close(sock);
181             break;
182         }
183 
184         if (waitBetweenRuns) {
185             latencies[iterations] = stopwatch.timeTakenUs();
186             state.SetIterationTime(static_cast<double>(latencies[iterations]) / 1.0e6L);
187             std::this_thread::sleep_for(std::chrono::milliseconds(10));
188             ++iterations;
189         }
190 
191         sockaddr_in6 client;
192         socklen_t clientlen = sizeof(client);
193         int accepted = accept4(listensocket, (sockaddr*) &client, &clientlen, SOCK_CLOEXEC);
194         if (accepted < 0) {
195             state.SkipWithError(StringPrintf("accept() failed with errno=%d", errno).c_str());
196             close(sock);
197             break;
198         }
199 
200         close(accepted);
201         close(sock);
202     }
203     close(listensocket);
204     // ALOGI("Finished test on port = %d", port);
205 
206     if (iterations > 0) {
207         latencies.resize(iterations);
208         sort(latencies.begin(), latencies.end());
209         state.SetLabel(StringPrintf("%lld", (long long) latencies[iterations * 9 / 10]));
210     }
211 }
212 
run(decltype(ipv4_loopback) benchmarkFunction,::benchmark::State & state,const bool waitBetweenRuns)213 static void run(decltype(ipv4_loopback) benchmarkFunction, ::benchmark::State& state,
214                 const bool waitBetweenRuns) {
215     benchmarkFunction(state, waitBetweenRuns);
216 }
217 
218 constexpr int MIN_THREADS = 1;
219 constexpr int MAX_THREADS = 1;
220 constexpr double MIN_TIME = 0.5 /* seconds */;
221 
222 // IPv4 benchmarks under no load
ipv4_no_load(::benchmark::State & state)223 static void ipv4_no_load(::benchmark::State& state) {
224     run(ipv4_loopback, state, true);
225 }
226 BENCHMARK(ipv4_no_load)->MinTime(MIN_TIME)->UseManualTime();
227 
228 // IPv4 benchmarks under high load
ipv4_high_load(::benchmark::State & state)229 static void ipv4_high_load(::benchmark::State& state) {
230     run(ipv4_loopback, state, false);
231 }
232 BENCHMARK(ipv4_high_load)->ThreadRange(MIN_THREADS, MAX_THREADS)->MinTime(MIN_TIME)->UseRealTime();
233 
234 // IPv6 raw connect() without using fwmark
ipv6_no_load(::benchmark::State & state)235 static void ipv6_no_load(::benchmark::State& state) {
236     run(ipv6_loopback, state, true);
237 }
238 BENCHMARK(ipv6_no_load)->MinTime(MIN_TIME)->UseManualTime();
239 
240 // IPv6 benchmarks under high load
ipv6_high_load(::benchmark::State & state)241 static void ipv6_high_load(::benchmark::State& state) {
242     run(ipv6_loopback, state, false);
243 }
244 BENCHMARK(ipv6_high_load)->ThreadRange(MIN_THREADS, MAX_THREADS)->MinTime(MIN_TIME)->UseRealTime();
245