diff options
Diffstat (limited to 'tests/binder/benchmarks')
-rw-r--r-- | tests/binder/benchmarks/Android.mk | 48 | ||||
-rw-r--r-- | tests/binder/benchmarks/binderAddInts.cpp | 458 |
2 files changed, 506 insertions, 0 deletions
diff --git a/tests/binder/benchmarks/Android.mk b/tests/binder/benchmarks/Android.mk new file mode 100644 index 00000000..53d35a07 --- /dev/null +++ b/tests/binder/benchmarks/Android.mk @@ -0,0 +1,48 @@ +# +# Copyright (C) 2010 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +ifneq ($(TARGET_SIMULATOR),true) # GTest needs STLport, which the simulator + # doesn't support + +LOCAL_PATH:= $(call my-dir) + +include $(CLEAR_VARS) +LOCAL_MODULE_TAGS := eng tests +LOCAL_MODULE_PATH := $(TARGET_OUT_DATA)/nativebenchmark + +LOCAL_STATIC_LIBRARIES += \ + libgtest \ + libgtest_main \ + libtestUtil + +LOCAL_SHARED_LIBRARIES += \ + libutils \ + libstlport \ + libbinder + +LOCAL_C_INCLUDES += \ + bionic \ + bionic/libstdc++/include \ + external/stlport/stlport \ + external/gtest/include \ + system/extras/tests/include \ + frameworks/base/include + +LOCAL_MODULE := binderAddInts +LOCAL_SRC_FILES := binderAddInts.cpp +include $(BUILD_EXECUTABLE) + +endif diff --git a/tests/binder/benchmarks/binderAddInts.cpp b/tests/binder/benchmarks/binderAddInts.cpp new file mode 100644 index 00000000..88937dc5 --- /dev/null +++ b/tests/binder/benchmarks/binderAddInts.cpp @@ -0,0 +1,458 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +/* + * Binder add integers benchmark + * + * Measures the rate at which a short binder IPC operation can be + * performed. The operation consists of the client sending a parcel + * that contains two integers. For each parcel that the server + * receives, it adds the two integers and sends the sum back to + * the client. + * + * This benchmark supports the following command-line options: + * + * -c cpu - bind client to specified cpu (default: unbound) + * -s cpu - bind server to specified cpu (default: unbound) + * -n num - perform IPC operation num times (default: 1000) + * -d time - delay specified amount of seconds after each + * IPC operation. (default 1e-3) + */ + +#include <cerrno> +#include <grp.h> +#include <iostream> +#include <libgen.h> +#include <time.h> +#include <unistd.h> + +#include <sys/syscall.h> +#include <sys/time.h> +#include <sys/types.h> +#include <sys/wait.h> + +#include <binder/IPCThreadState.h> +#include <binder/ProcessState.h> +#include <binder/IServiceManager.h> +#include <utils/Log.h> +#include <testUtil.h> + +using namespace android; +using namespace std; + +const int unbound = -1; // Indicator for a thread not bound to a specific CPU + +String16 serviceName("test.binderAddInts"); + +struct options { + int serverCPU; + int clientCPU; + unsigned int iterations; + float iterDelay; // End of iteration delay in seconds +} options = { // Set defaults + unbound, // Server CPU + unbound, // Client CPU + 1000, // Iterations + 1e-3, // End of iteration delay +}; + +class AddIntsService : public BBinder +{ + public: + AddIntsService(int cpu = unbound); + virtual ~AddIntsService() {}; + + enum command { + ADD_INTS = 0x120, + }; + + virtual status_t onTransact(uint32_t code, + const Parcel& data, Parcel* reply, + uint32_t flags = 0); + + private: + int cpu_; +}; + +// Workaround for missing sched_setaffinity(2) and getcpu(2) +#define CPU_SETSIZE 1024 +struct getcpu_cache; +typedef struct { uint64_t bits[CPU_SETSIZE / 64]; } cpu_set_t; +static int sched_getaffinity(pid_t pid, unsigned int cpusetsize, + cpu_set_t *set); +static int sched_setaffinity(pid_t pid, unsigned int cpusetsize, + cpu_set_t *mask); +static int getcpu(unsigned *cpu, unsigned *node, struct getcpu_cache *tcache); +static int CPU_ISSET(int cpu, const cpu_set_t *set); +static void CPU_SET(int cpu, cpu_set_t *set); +static void CPU_ZERO(cpu_set_t *set); + +// File scope function prototypes +static void server(void); +static void client(void); +static void bindCPU(unsigned int cpu); +static ostream &operator<<(ostream &stream, const String16& str); +static ostream &operator<<(ostream &stream, const cpu_set_t& set); + +int main(int argc, char *argv[]) +{ + int rv; + + // Determine CPUs available for use. + // This testcase limits its self to using CPUs that were + // available at the start of the benchmark. + cpu_set_t availCPUs; + if ((rv = sched_getaffinity(0, sizeof(availCPUs), &availCPUs)) != 0) { + cerr << "sched_getaffinity failure, rv: " << rv + << " errno: " << errno << endl; + exit(1); + } + + // Parse command line arguments + int opt; + while ((opt = getopt(argc, argv, "s:c:n:d:?")) != -1) { + char *chptr; // character pointer for command-line parsing + + switch (opt) { + case 'c': // client CPU + case 's': { // server CPU + // Parse the CPU number + int cpu = strtoul(optarg, &chptr, 10); + if (*chptr != '\0') { + cerr << "Invalid cpu specified for -" << (char) opt + << " option of: " << optarg << endl; + exit(2); + } + + // Is the CPU available? + if (!CPU_ISSET(cpu, &availCPUs)) { + cerr << "CPU " << optarg << " not currently available" << endl; + cerr << " Available CPUs: " << availCPUs << endl; + exit(3); + } + + // Record the choice + *((opt == 'c') ? &options.clientCPU : &options.serverCPU) = cpu; + break; + } + + case 'n': // iterations + options.iterations = strtoul(optarg, &chptr, 10); + if (*chptr != '\0') { + cerr << "Invalid iterations specified of: " << optarg << endl; + exit(4); + } + if (options.iterations < 1) { + cerr << "Less than 1 iteration specified by: " + << optarg << endl; + exit(5); + } + break; + + case 'd': // Delay between each iteration + options.iterDelay = strtod(optarg, &chptr); + if ((*chptr != '\0') || (options.iterDelay < 0.0)) { + cerr << "Invalid delay specified of: " << optarg << endl; + exit(6); + } + break; + + case '?': + default: + cerr << basename(argv[0]) << " [options]" << endl; + cerr << " options:" << endl; + cerr << " -s cpu - server CPU number" << endl; + cerr << " -c cpu - client CPU number" << endl; + cerr << " -n num - iterations" << endl; + cerr << " -d time - delay after operation in seconds" << endl; + exit(((optopt == 0) || (optopt == '?')) ? 0 : 7); + } + } + + // Display selected options + cout << "serverCPU: "; + if (options.serverCPU == unbound) { + cout << " unbound"; + } else { + cout << options.serverCPU; + } + cout << endl; + cout << "clientCPU: "; + if (options.clientCPU == unbound) { + cout << " unbound"; + } else { + cout << options.clientCPU; + } + cout << endl; + cout << "iterations: " << options.iterations << endl; + cout << "iterDelay: " << options.iterDelay << endl; + + // Fork client, use this process as server + fflush(stdout); + switch (pid_t pid = fork()) { + case 0: // Child + client(); + return 0; + + default: // Parent + server(); + + // Wait for all children to end + do { + int stat; + rv = wait(&stat); + if ((rv == -1) && (errno == ECHILD)) { break; } + if (rv == -1) { + cerr << "wait failed, rv: " << rv << " errno: " + << errno << endl; + perror(NULL); + exit(8); + } + } while (1); + return 0; + + case -1: // Error + exit(9); + } + + return 0; +} + +static void server(void) +{ + int rv; + + // Add the service + sp<ProcessState> proc(ProcessState::self()); + sp<IServiceManager> sm = defaultServiceManager(); + if ((rv = sm->addService(serviceName, + new AddIntsService(options.serverCPU))) != 0) { + cerr << "addService " << serviceName << " failed, rv: " << rv + << " errno: " << errno << endl; + } + + // Start threads to handle server work + proc->startThreadPool(); +} + +static void client(void) +{ + int rv; + sp<IServiceManager> sm = defaultServiceManager(); + double min = FLT_MAX, max = 0.0, total = 0.0; // Time in seconds for all + // the IPC calls. + + // If needed bind to client CPU + if (options.clientCPU != unbound) { bindCPU(options.clientCPU); } + + // Attach to service + sp<IBinder> binder; + do { + binder = sm->getService(serviceName); + if (binder != 0) break; + cout << serviceName << " not published, waiting..." << endl; + usleep(500000); // 0.5 s + } while(true); + + // Perform the IPC operations + for (unsigned int iter = 0; iter < options.iterations; iter++) { + Parcel send, reply; + + // Create parcel to be sent. Will use the iteration cound + // and the iteration count + 3 as the two integer values + // to be sent. + int val1 = iter; + int val2 = iter + 3; + int expected = val1 + val2; // Expect to get the sum back + send.writeInt32(val1); + send.writeInt32(val2); + + // Send the parcel, while timing how long it takes for + // the answer to return. + struct timespec start; + clock_gettime(CLOCK_MONOTONIC, &start); + if ((rv = binder->transact(AddIntsService::ADD_INTS, + send, &reply)) != 0) { + cerr << "binder->transact failed, rv: " << rv + << " errno: " << errno << endl; + exit(10); + } + struct timespec current; + clock_gettime(CLOCK_MONOTONIC, ¤t); + + // Calculate how long this operation took and update the stats + struct timespec deltaTimespec = tsDelta(&start, ¤t); + double delta = ts2double(&deltaTimespec); + min = (delta < min) ? delta : min; + max = (delta > max) ? delta : max; + total += delta; + int result = reply.readInt32(); + if (result != (int) (iter + iter + 3)) { + cerr << "Unexpected result for iteration " << iter << endl; + cerr << " result: " << result << endl; + cerr << "expected: " << expected << endl; + } + + if (options.iterDelay > 0.0) { delay(options.iterDelay); } + } + + // Display the results + cout << "Time per iteration min: " << min + << " avg: " << (total / options.iterations) + << " max: " << max + << endl; +} + +AddIntsService::AddIntsService(int cpu): cpu_(cpu) { + if (cpu != unbound) { bindCPU(cpu); } +}; + +// Server function that handles parcels received from the client +status_t AddIntsService::onTransact(uint32_t code, const Parcel &data, + Parcel* reply, uint32_t flags) { + int val1, val2; + status_t rv(0); + int cpu; + + // If server bound to a particular CPU, check that + // were executing on that CPU. + if (cpu_ != unbound) { + getcpu((unsigned int *) &cpu, NULL, NULL); + if (cpu != cpu_) { + cerr << "server onTransact on CPU " << cpu << " expected CPU " + << cpu_ << endl; + exit(20); + } + } + + // Perform the requested operation + switch (code) { + case ADD_INTS: + val1 = data.readInt32(); + val2 = data.readInt32(); + reply->writeInt32(val1 + val2); + break; + + default: + cerr << "server onTransact unknown code, code: " << code << endl; + exit(21); + } + + return rv; +} + +static void bindCPU(unsigned int cpu) +{ + int rv; + cpu_set_t cpuset; + + CPU_ZERO(&cpuset); + CPU_SET(cpu, &cpuset); + rv = sched_setaffinity(0, sizeof(cpuset), &cpuset); + + if (rv != 0) { + cerr << "bindCPU failed, rv: " << rv << " errno: " << errno << endl; + perror(NULL); + exit(30); + } +} + +static ostream &operator<<(ostream &stream, const String16& str) +{ + for (unsigned int n1 = 0; n1 < str.size(); n1++) { + if ((str[n1] > 0x20) && (str[n1] < 0x80)) { + stream << (char) str[n1]; + } else { + stream << '~'; + } + } + + return stream; +} + +static ostream &operator<<(ostream &stream, const cpu_set_t& set) +{ + for (unsigned int n1 = 0; n1 < CPU_SETSIZE; n1++) { + if (CPU_ISSET(n1, &set)) { + if (n1 != 0) { stream << ' '; } + stream << n1; + } + } + + return stream; +} + +// ======== Local implementation of system calls with missing bionic call stubs +static int sched_getaffinity(pid_t pid, unsigned int cpusetsize, + cpu_set_t *set) +{ + int rv; + + rv = syscall(__NR_sched_getaffinity, pid, cpusetsize, set); + if (rv < 0) { return rv; } + + // Kernel implementation of sched_getaffinity() returns the number + // of bytes in the set that it set. Set the rest of our set bits + // to 0. + memset(((char *) set) + rv, 0x00, sizeof(cpu_set_t) - rv); + + return 0; +} + +static int sched_setaffinity(pid_t pid, unsigned int cpusetsize, + cpu_set_t *mask) +{ + int rv; + + rv = syscall(__NR_sched_setaffinity, pid, cpusetsize, mask); + + return rv; +} + +static int getcpu(unsigned *cpu, unsigned *node, struct getcpu_cache *tcache) +{ + int rv; + + rv = syscall(345, cpu, node, tcache); + + return rv; +} + +static int CPU_ISSET(int cpu, const cpu_set_t *set) +{ + if (cpu < 0) { return 0; } + if ((unsigned) cpu >= (sizeof(cpu_set_t) * CHAR_BIT)) { return 0; } + + if ((*((uint64_t *)set + (cpu / 64))) & (1ULL << (cpu % 64))) { + return true; + } + + return false; +} + +static void CPU_SET(int cpu, cpu_set_t *set) +{ + if (cpu < 0) { return; } + if ((unsigned) cpu > (sizeof(cpu_set_t) * CHAR_BIT)) { return; } + + *((uint64_t *)set + (cpu / 64)) |= 1ULL << (cpu % 64); +} + +static void CPU_ZERO(cpu_set_t *set) +{ + memset(set, 0x00, sizeof(cpu_set_t)); +} |