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