2 * Copyright (C) 2010 The Android Open Source Project
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
19 * Binder add integers benchmark
21 * Measures the rate at which a short binder IPC operation can be
22 * performed. The operation consists of the client sending a parcel
23 * that contains two integers. For each parcel that the server
24 * receives, it adds the two integers and sends the sum back to
27 * This benchmark supports the following command-line options:
29 * -c cpu - bind client to specified cpu (default: unbound)
30 * -s cpu - bind server to specified cpu (default: unbound)
31 * -n num - perform IPC operation num times (default: 1000)
32 * -d time - delay specified amount of seconds after each
33 * IPC operation. (default 1e-3)
44 #include <sys/syscall.h>
46 #include <sys/types.h>
49 #include <binder/IPCThreadState.h>
50 #include <binder/ProcessState.h>
51 #include <binder/IServiceManager.h>
52 #include <utils/Log.h>
55 using namespace android;
58 const int unbound = -1; // Indicator for a thread not bound to a specific CPU
60 String16 serviceName("test.binderAddInts");
65 unsigned int iterations;
66 float iterDelay; // End of iteration delay in seconds
67 } options = { // Set defaults
68 unbound, // Server CPU
69 unbound, // Client CPU
71 1e-3, // End of iteration delay
74 class AddIntsService : public BBinder
77 AddIntsService(int cpu = unbound);
78 virtual ~AddIntsService() {}
84 virtual status_t onTransact(uint32_t code,
85 const Parcel& data, Parcel* reply,
94 friend std::ostream& operator<<(std::ostream& stream, const Duration& d) {
95 static const char *SUFFIXES[] = {"s", "ms", "us", "ns"};
97 double temp = d.value;
98 while (temp < .1 && suffix < 3) {
102 stream << temp << SUFFIXES[suffix];
107 // File scope function prototypes
108 static bool server(void);
109 static void client(void);
110 static void bindCPU(unsigned int cpu);
111 static ostream &operator<<(ostream &stream, const String16& str);
112 static ostream &operator<<(ostream &stream, const cpu_set_t& set);
114 int main(int argc, char *argv[])
118 // Determine CPUs available for use.
119 // This testcase limits its self to using CPUs that were
120 // available at the start of the benchmark.
122 if ((rv = sched_getaffinity(0, sizeof(availCPUs), &availCPUs)) != 0) {
123 cerr << "sched_getaffinity failure, rv: " << rv
124 << " errno: " << errno << endl;
128 // Parse command line arguments
130 while ((opt = getopt(argc, argv, "s:c:n:d:?")) != -1) {
131 char *chptr; // character pointer for command-line parsing
134 case 'c': // client CPU
135 case 's': { // server CPU
136 // Parse the CPU number
137 int cpu = strtoul(optarg, &chptr, 10);
138 if (*chptr != '\0') {
139 cerr << "Invalid cpu specified for -" << (char) opt
140 << " option of: " << optarg << endl;
144 // Is the CPU available?
145 if (!CPU_ISSET(cpu, &availCPUs)) {
146 cerr << "CPU " << optarg << " not currently available" << endl;
147 cerr << " Available CPUs: " << availCPUs << endl;
152 *((opt == 'c') ? &options.clientCPU : &options.serverCPU) = cpu;
156 case 'n': // iterations
157 options.iterations = strtoul(optarg, &chptr, 10);
158 if (*chptr != '\0') {
159 cerr << "Invalid iterations specified of: " << optarg << endl;
162 if (options.iterations < 1) {
163 cerr << "Less than 1 iteration specified by: "
169 case 'd': // Delay between each iteration
170 options.iterDelay = strtod(optarg, &chptr);
171 if ((*chptr != '\0') || (options.iterDelay < 0.0)) {
172 cerr << "Invalid delay specified of: " << optarg << endl;
179 cerr << basename(argv[0]) << " [options]" << endl;
180 cerr << " options:" << endl;
181 cerr << " -s cpu - server CPU number" << endl;
182 cerr << " -c cpu - client CPU number" << endl;
183 cerr << " -n num - iterations" << endl;
184 cerr << " -d time - delay after operation in seconds" << endl;
185 exit(((optopt == 0) || (optopt == '?')) ? 0 : 7);
189 // Display selected options
190 cout << "serverCPU: ";
191 if (options.serverCPU == unbound) {
194 cout << options.serverCPU;
197 cout << "clientCPU: ";
198 if (options.clientCPU == unbound) {
201 cout << options.clientCPU;
204 cout << "iterations: " << options.iterations << endl;
205 cout << "iterDelay: " << options.iterDelay << endl;
207 // Fork client, use this process as server
209 switch (pid_t pid = fork()) {
215 if (!server()) { break; }
217 // Wait for all children to end
221 if ((rv == -1) && (errno == ECHILD)) { break; }
223 cerr << "wait failed, rv: " << rv << " errno: "
238 static bool server(void)
243 sp<ProcessState> proc(ProcessState::self());
244 sp<IServiceManager> sm = defaultServiceManager();
245 if ((rv = sm->addService(serviceName,
246 new AddIntsService(options.serverCPU))) != 0) {
247 cerr << "addService " << serviceName << " failed, rv: " << rv
248 << " errno: " << errno << endl;
252 // Start threads to handle server work
253 proc->startThreadPool();
257 static void client(void)
260 sp<IServiceManager> sm = defaultServiceManager();
261 double min = FLT_MAX, max = 0.0, total = 0.0; // Time in seconds for all
264 // If needed bind to client CPU
265 if (options.clientCPU != unbound) { bindCPU(options.clientCPU); }
269 for (int i = 0; i < 3; i++) {
270 binder = sm->getService(serviceName);
271 if (binder != 0) break;
272 cout << serviceName << " not published, waiting..." << endl;
273 usleep(500000); // 0.5 s
277 cout << serviceName << " failed to publish, aborting" << endl;
281 // Perform the IPC operations
282 for (unsigned int iter = 0; iter < options.iterations; iter++) {
285 // Create parcel to be sent. Will use the iteration cound
286 // and the iteration count + 3 as the two integer values
290 int expected = val1 + val2; // Expect to get the sum back
291 send.writeInt32(val1);
292 send.writeInt32(val2);
294 // Send the parcel, while timing how long it takes for
295 // the answer to return.
296 struct timespec start;
297 clock_gettime(CLOCK_MONOTONIC, &start);
298 if ((rv = binder->transact(AddIntsService::ADD_INTS,
299 send, &reply)) != 0) {
300 cerr << "binder->transact failed, rv: " << rv
301 << " errno: " << errno << endl;
304 struct timespec current;
305 clock_gettime(CLOCK_MONOTONIC, ¤t);
307 // Calculate how long this operation took and update the stats
308 struct timespec deltaTimespec = tsDelta(&start, ¤t);
309 double delta = ts2double(&deltaTimespec);
310 min = (delta < min) ? delta : min;
311 max = (delta > max) ? delta : max;
313 int result = reply.readInt32();
314 if (result != (int) (iter + iter + 3)) {
315 cerr << "Unexpected result for iteration " << iter << endl;
316 cerr << " result: " << result << endl;
317 cerr << "expected: " << expected << endl;
320 if (options.iterDelay > 0.0) { testDelaySpin(options.iterDelay); }
323 // Display the results
324 cout << fixed << setprecision(2)
325 << "Time per iteration min: " << Duration{min}
326 << " avg: " << Duration{total / options.iterations}
327 << " max: " << Duration{max}
331 AddIntsService::AddIntsService(int cpu): cpu_(cpu) {
332 if (cpu != unbound) { bindCPU(cpu); }
335 // Server function that handles parcels received from the client
336 status_t AddIntsService::onTransact(uint32_t code, const Parcel &data,
337 Parcel* reply, uint32_t /* flags */) {
342 // If server bound to a particular CPU, check that
343 // were executing on that CPU.
344 if (cpu_ != unbound) {
345 cpu = sched_getcpu();
347 cerr << "server onTransact on CPU " << cpu << " expected CPU "
353 // Perform the requested operation
356 val1 = data.readInt32();
357 val2 = data.readInt32();
358 reply->writeInt32(val1 + val2);
362 cerr << "server onTransact unknown code, code: " << code << endl;
369 static void bindCPU(unsigned int cpu)
375 CPU_SET(cpu, &cpuset);
376 rv = sched_setaffinity(0, sizeof(cpuset), &cpuset);
379 cerr << "bindCPU failed, rv: " << rv << " errno: " << errno << endl;
385 static ostream &operator<<(ostream &stream, const String16& str)
387 for (unsigned int n1 = 0; n1 < str.size(); n1++) {
388 if ((str[n1] > 0x20) && (str[n1] < 0x80)) {
389 stream << (char) str[n1];
398 static ostream &operator<<(ostream &stream, const cpu_set_t& set)
400 for (unsigned int n1 = 0; n1 < CPU_SETSIZE; n1++) {
401 if (CPU_ISSET(n1, &set)) {
402 if (n1 != 0) { stream << ' '; }