Home | History | Annotate | Download | only in benchmarks
      1 /*
      2  * Copyright (C) 2010 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 
     18 /*
     19  * Binder add integers benchmark
     20  *
     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
     25  * the client.
     26  *
     27  * This benchmark supports the following command-line options:
     28  *
     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)
     34  */
     35 
     36 #include <cerrno>
     37 #include <grp.h>
     38 #include <iostream>
     39 #include <libgen.h>
     40 #include <time.h>
     41 #include <unistd.h>
     42 
     43 #include <sys/syscall.h>
     44 #include <sys/time.h>
     45 #include <sys/types.h>
     46 #include <sys/wait.h>
     47 
     48 #include <binder/IPCThreadState.h>
     49 #include <binder/ProcessState.h>
     50 #include <binder/IServiceManager.h>
     51 #include <utils/Log.h>
     52 #include <testUtil.h>
     53 
     54 using namespace android;
     55 using namespace std;
     56 
     57 const int unbound = -1; // Indicator for a thread not bound to a specific CPU
     58 
     59 String16 serviceName("test.binderAddInts");
     60 
     61 struct options {
     62     int serverCPU;
     63     int clientCPU;
     64     unsigned int iterations;
     65     float        iterDelay; // End of iteration delay in seconds
     66 } options = { // Set defaults
     67     unbound, // Server CPU
     68     unbound, // Client CPU
     69     1000,    // Iterations
     70     1e-3,    // End of iteration delay
     71 };
     72 
     73 class AddIntsService : public BBinder
     74 {
     75   public:
     76     AddIntsService(int cpu = unbound);
     77     virtual ~AddIntsService() {}
     78 
     79     enum command {
     80         ADD_INTS = 0x120,
     81     };
     82 
     83     virtual status_t onTransact(uint32_t code,
     84                                 const Parcel& data, Parcel* reply,
     85                                 uint32_t flags = 0);
     86 
     87   private:
     88     int cpu_;
     89 };
     90 
     91 // File scope function prototypes
     92 static void server(void);
     93 static void client(void);
     94 static void bindCPU(unsigned int cpu);
     95 static ostream &operator<<(ostream &stream, const String16& str);
     96 static ostream &operator<<(ostream &stream, const cpu_set_t& set);
     97 
     98 int main(int argc, char *argv[])
     99 {
    100     int rv;
    101 
    102     // Determine CPUs available for use.
    103     // This testcase limits its self to using CPUs that were
    104     // available at the start of the benchmark.
    105     cpu_set_t availCPUs;
    106     if ((rv = sched_getaffinity(0, sizeof(availCPUs), &availCPUs)) != 0) {
    107         cerr << "sched_getaffinity failure, rv: " << rv
    108             << " errno: " << errno << endl;
    109         exit(1);
    110     }
    111 
    112     // Parse command line arguments
    113     int opt;
    114     while ((opt = getopt(argc, argv, "s:c:n:d:?")) != -1) {
    115         char *chptr; // character pointer for command-line parsing
    116 
    117         switch (opt) {
    118         case 'c': // client CPU
    119         case 's': { // server CPU
    120             // Parse the CPU number
    121             int cpu = strtoul(optarg, &chptr, 10);
    122             if (*chptr != '\0') {
    123                 cerr << "Invalid cpu specified for -" << (char) opt
    124                     << " option of: " << optarg << endl;
    125                 exit(2);
    126             }
    127 
    128             // Is the CPU available?
    129             if (!CPU_ISSET(cpu, &availCPUs)) {
    130                 cerr << "CPU " << optarg << " not currently available" << endl;
    131                 cerr << "  Available CPUs: " << availCPUs << endl;
    132                 exit(3);
    133             }
    134 
    135             // Record the choice
    136             *((opt == 'c') ? &options.clientCPU : &options.serverCPU) = cpu;
    137             break;
    138         }
    139 
    140         case 'n': // iterations
    141             options.iterations = strtoul(optarg, &chptr, 10);
    142             if (*chptr != '\0') {
    143                 cerr << "Invalid iterations specified of: " << optarg << endl;
    144                 exit(4);
    145             }
    146             if (options.iterations < 1) {
    147                 cerr << "Less than 1 iteration specified by: "
    148                     << optarg << endl;
    149                 exit(5);
    150             }
    151             break;
    152 
    153         case 'd': // Delay between each iteration
    154             options.iterDelay = strtod(optarg, &chptr);
    155             if ((*chptr != '\0') || (options.iterDelay < 0.0)) {
    156                 cerr << "Invalid delay specified of: " << optarg << endl;
    157                 exit(6);
    158             }
    159             break;
    160 
    161         case '?':
    162         default:
    163             cerr << basename(argv[0]) << " [options]" << endl;
    164             cerr << "  options:" << endl;
    165             cerr << "    -s cpu - server CPU number" << endl;
    166             cerr << "    -c cpu - client CPU number" << endl;
    167             cerr << "    -n num - iterations" << endl;
    168             cerr << "    -d time - delay after operation in seconds" << endl;
    169             exit(((optopt == 0) || (optopt == '?')) ? 0 : 7);
    170         }
    171     }
    172 
    173     // Display selected options
    174     cout << "serverCPU: ";
    175     if (options.serverCPU == unbound) {
    176         cout << " unbound";
    177     } else {
    178         cout << options.serverCPU;
    179     }
    180     cout << endl;
    181     cout << "clientCPU: ";
    182     if (options.clientCPU == unbound) {
    183         cout << " unbound";
    184     } else {
    185         cout << options.clientCPU;
    186     }
    187     cout << endl;
    188     cout << "iterations: " << options.iterations << endl;
    189     cout << "iterDelay: " << options.iterDelay << endl;
    190 
    191     // Fork client, use this process as server
    192     fflush(stdout);
    193     switch (pid_t pid = fork()) {
    194     case 0: // Child
    195         client();
    196         return 0;
    197 
    198     default: // Parent
    199         server();
    200 
    201         // Wait for all children to end
    202         do {
    203             int stat;
    204             rv = wait(&stat);
    205             if ((rv == -1) && (errno == ECHILD)) { break; }
    206             if (rv == -1) {
    207                 cerr << "wait failed, rv: " << rv << " errno: "
    208                     << errno << endl;
    209                 perror(NULL);
    210                 exit(8);
    211             }
    212         } while (1);
    213         return 0;
    214 
    215     case -1: // Error
    216         exit(9);
    217     }
    218 
    219     return 0;
    220 }
    221 
    222 static void server(void)
    223 {
    224     int rv;
    225 
    226     // Add the service
    227     sp<ProcessState> proc(ProcessState::self());
    228     sp<IServiceManager> sm = defaultServiceManager();
    229     if ((rv = sm->addService(serviceName,
    230         new AddIntsService(options.serverCPU))) != 0) {
    231         cerr << "addService " << serviceName << " failed, rv: " << rv
    232             << " errno: " << errno << endl;
    233     }
    234 
    235     // Start threads to handle server work
    236     proc->startThreadPool();
    237 }
    238 
    239 static void client(void)
    240 {
    241     int rv;
    242     sp<IServiceManager> sm = defaultServiceManager();
    243     double min = FLT_MAX, max = 0.0, total = 0.0; // Time in seconds for all
    244                                                   // the IPC calls.
    245 
    246     // If needed bind to client CPU
    247     if (options.clientCPU != unbound) { bindCPU(options.clientCPU); }
    248 
    249     // Attach to service
    250     sp<IBinder> binder;
    251     do {
    252         binder = sm->getService(serviceName);
    253         if (binder != 0) break;
    254         cout << serviceName << " not published, waiting..." << endl;
    255         usleep(500000); // 0.5 s
    256     } while(true);
    257 
    258     // Perform the IPC operations
    259     for (unsigned int iter = 0; iter < options.iterations; iter++) {
    260         Parcel send, reply;
    261 
    262         // Create parcel to be sent.  Will use the iteration cound
    263         // and the iteration count + 3 as the two integer values
    264         // to be sent.
    265         int val1 = iter;
    266         int val2 = iter + 3;
    267         int expected = val1 + val2;  // Expect to get the sum back
    268         send.writeInt32(val1);
    269         send.writeInt32(val2);
    270 
    271         // Send the parcel, while timing how long it takes for
    272         // the answer to return.
    273         struct timespec start;
    274         clock_gettime(CLOCK_MONOTONIC, &start);
    275         if ((rv = binder->transact(AddIntsService::ADD_INTS,
    276             send, &reply)) != 0) {
    277             cerr << "binder->transact failed, rv: " << rv
    278                 << " errno: " << errno << endl;
    279             exit(10);
    280         }
    281         struct timespec current;
    282         clock_gettime(CLOCK_MONOTONIC, &current);
    283 
    284         // Calculate how long this operation took and update the stats
    285         struct timespec deltaTimespec = tsDelta(&start, &current);
    286         double delta = ts2double(&deltaTimespec);
    287         min = (delta < min) ? delta : min;
    288         max = (delta > max) ? delta : max;
    289         total += delta;
    290         int result = reply.readInt32();
    291         if (result != (int) (iter + iter + 3)) {
    292             cerr << "Unexpected result for iteration " << iter << endl;
    293             cerr << "  result: " << result << endl;
    294             cerr << "expected: " << expected << endl;
    295         }
    296 
    297         if (options.iterDelay > 0.0) { testDelaySpin(options.iterDelay); }
    298     }
    299 
    300     // Display the results
    301     cout << "Time per iteration min: " << min
    302         << " avg: " << (total / options.iterations)
    303         << " max: " << max
    304         << endl;
    305 }
    306 
    307 AddIntsService::AddIntsService(int cpu): cpu_(cpu) {
    308     if (cpu != unbound) { bindCPU(cpu); }
    309 }
    310 
    311 // Server function that handles parcels received from the client
    312 status_t AddIntsService::onTransact(uint32_t code, const Parcel &data,
    313                                     Parcel* reply, uint32_t flags) {
    314     int val1, val2;
    315     status_t rv(0);
    316     int cpu;
    317 
    318     // If server bound to a particular CPU, check that
    319     // were executing on that CPU.
    320     if (cpu_ != unbound) {
    321         cpu = sched_getcpu();
    322         if (cpu != cpu_) {
    323             cerr << "server onTransact on CPU " << cpu << " expected CPU "
    324                   << cpu_ << endl;
    325             exit(20);
    326         }
    327     }
    328 
    329     // Perform the requested operation
    330     switch (code) {
    331     case ADD_INTS:
    332         val1 = data.readInt32();
    333         val2 = data.readInt32();
    334         reply->writeInt32(val1 + val2);
    335         break;
    336 
    337     default:
    338       cerr << "server onTransact unknown code, code: " << code << endl;
    339       exit(21);
    340     }
    341 
    342     return rv;
    343 }
    344 
    345 static void bindCPU(unsigned int cpu)
    346 {
    347     int rv;
    348     cpu_set_t cpuset;
    349 
    350     CPU_ZERO(&cpuset);
    351     CPU_SET(cpu, &cpuset);
    352     rv = sched_setaffinity(0, sizeof(cpuset), &cpuset);
    353 
    354     if (rv != 0) {
    355         cerr << "bindCPU failed, rv: " << rv << " errno: " << errno << endl;
    356         perror(NULL);
    357         exit(30);
    358     }
    359 }
    360 
    361 static ostream &operator<<(ostream &stream, const String16& str)
    362 {
    363     for (unsigned int n1 = 0; n1 < str.size(); n1++) {
    364         if ((str[n1] > 0x20) && (str[n1] < 0x80)) {
    365             stream << (char) str[n1];
    366         } else {
    367             stream << '~';
    368         }
    369     }
    370 
    371     return stream;
    372 }
    373 
    374 static ostream &operator<<(ostream &stream, const cpu_set_t& set)
    375 {
    376     for (unsigned int n1 = 0; n1 < CPU_SETSIZE; n1++) {
    377         if (CPU_ISSET(n1, &set)) {
    378             if (n1 != 0) { stream << ' '; }
    379             stream << n1;
    380         }
    381     }
    382 
    383     return stream;
    384 }
    385