/* * Copyright (C) 2013 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. */ #include "bandwidth.h" #include <ctype.h> #include <pthread.h> #include <sched.h> #include <sys/resource.h> #include <sys/time.h> #include <unistd.h> #include <map> #include <vector> typedef struct { const char *name; bool int_type; } option_t; option_t bandwidth_opts[] = { { "size", true }, { "num_warm_loops", true }, { "num_loops", true }, { "type", false }, { NULL, false }, }; option_t per_core_opts[] = { { "size", true }, { "num_warm_loops", true}, { "num_loops", true }, { "type", false }, { NULL, false }, }; option_t multithread_opts[] = { { "size", true }, { "num_warm_loops", true}, { "num_loops", true }, { "type", false }, { "num_threads", true }, { NULL, false }, }; typedef union { int int_value; const char *char_value; } arg_value_t; typedef std::map<const char*, arg_value_t> arg_t; bool processBandwidthOptions(int argc, char** argv, option_t options[], arg_t *values) { for (int i = 1; i < argc; i++) { if (argv[i][0] == '-' && argv[i][1] == '-' && !isdigit(argv[i][2])) { char *arg = &argv[i][2]; for (int j = 0; options[j].name != NULL; j++) { if (strcmp(arg, options[j].name) == 0) { const char *name = options[j].name; if (i == argc - 1) { printf("The option --%s requires an argument.\n", name); return false; } if (options[j].int_type) { (*values)[name].int_value = strtol(argv[++i], NULL, 0); } else { (*values)[name].char_value = argv[++i]; } } } } } return true; } BandwidthBenchmark *createBandwidthBenchmarkObject(arg_t values) { BandwidthBenchmark *bench = NULL; const char *name = values["type"].char_value; size_t size = 0; if (values.count("size") > 0) { size = values["size"].int_value; } if (strcmp(name, "copy_ldrd_strd") == 0) { bench = new CopyLdrdStrdBenchmark(); } else if (strcmp(name, "copy_ldmia_stmia") == 0) { bench = new CopyLdmiaStmiaBenchmark(); } else if (strcmp(name, "copy_vld1_vst1") == 0) { bench = new CopyVld1Vst1Benchmark(); } else if (strcmp(name, "copy_vldr_vstr") == 0) { bench = new CopyVldrVstrBenchmark(); } else if (strcmp(name, "copy_vldmia_vstmia") == 0) { bench = new CopyVldmiaVstmiaBenchmark(); } else if (strcmp(name, "memcpy") == 0) { bench = new MemcpyBenchmark(); } else if (strcmp(name, "write_strd") == 0) { bench = new WriteStrdBenchmark(); } else if (strcmp(name, "write_stmia") == 0) { bench = new WriteStmiaBenchmark(); } else if (strcmp(name, "write_vst1") == 0) { bench = new WriteVst1Benchmark(); } else if (strcmp(name, "write_vstr") == 0) { bench = new WriteVstrBenchmark(); } else if (strcmp(name, "write_vstmia") == 0) { bench = new WriteVstmiaBenchmark(); } else if (strcmp(name, "memset") == 0) { bench = new MemsetBenchmark(); } else if (strcmp(name, "read_ldrd") == 0) { bench = new ReadLdrdBenchmark(); } else if (strcmp(name, "read_ldmia") == 0) { bench = new ReadLdmiaBenchmark(); } else if (strcmp(name, "read_vld1") == 0) { bench = new ReadVld1Benchmark(); } else if (strcmp(name, "read_vldr") == 0) { bench = new ReadVldrBenchmark(); } else if (strcmp(name, "read_vldmia") == 0) { bench = new ReadVldmiaBenchmark(); } else { printf("Unknown type name %s\n", name); return NULL; } if (!bench->setSize(size)) { printf("Failed to allocate buffers for benchmark.\n"); return NULL; } if (values.count("num_warm_loops") > 0) { bench->set_num_loops(values["num_warm_loops"].int_value); } if (values.count("num_loops") > 0) { bench->set_num_loops(values["num_loops"].int_value); } return bench; } bool getAvailCpus(std::vector<int> *cpu_list) { cpu_set_t cpuset; CPU_ZERO(&cpuset); if (sched_getaffinity(0, sizeof(cpuset), &cpuset) != 0) { perror("sched_getaffinity failed."); return false; } for (int i = 0; i < CPU_SETSIZE; i++) { if (CPU_ISSET(i, &cpuset)) { cpu_list->push_back(i); } } return true; } typedef struct { int core; BandwidthBenchmark *bench; double avg_mb; volatile bool *run; } thread_arg_t; void *runBandwidthThread(void *data) { thread_arg_t *arg = reinterpret_cast<thread_arg_t *>(data); if (arg->core >= 0) { cpu_set_t cpuset; CPU_ZERO(&cpuset); CPU_SET(arg->core, &cpuset); if (sched_setaffinity(0, sizeof(cpuset), &cpuset) != 0) { perror("sched_setaffinity failed"); return NULL; } } // Spinloop waiting for the run variable to get set to true. while (!*arg->run) { } double avg_mb = 0; for (int run = 1; ; run++) { arg->bench->run(); if (!*arg->run) { // Throw away the last data point since it's possible not // all of the threads are running at this point. break; } avg_mb = (avg_mb/run) * (run-1) + arg->bench->mb_per_sec()/run; } arg->avg_mb = avg_mb; return NULL; } bool processThreadArgs(int argc, char** argv, option_t options[], arg_t *values) { // Use some smaller values for the number of loops. (*values)["num_warm_loops"].int_value = 1000000; (*values)["num_loops"].int_value = 10000000; if (!processBandwidthOptions(argc, argv, options, values)) { return false; } if (values->count("size") > 0 && ((*values)["size"].int_value % 64) != 0) { printf("The size values must be a multiple of 64.\n"); return false; } if (values->count("type") == 0) { printf("Must specify the type value.\n"); return false; } BandwidthBenchmark *bench = createBandwidthBenchmarkObject(*values); if (!bench) { return false; } if (setpriority(PRIO_PROCESS, 0, -20)) { perror("Unable to raise priority of process."); return false; } printf("Calculating optimum run time...\n"); nsecs_t t = system_time(); bench->run(); t = system_time() - t; // Since this is only going to be running single threaded, assume that // if the number is set to ten times this value, we should get at least // a couple of samples per thread. int run_time = int((t/1000000000.0)*10 + 0.5) + 5; (*values)["run_time"].int_value = run_time; (*values)["size"].int_value = bench->size(); (*values)["num_warm_loops"].int_value = bench->num_warm_loops(); (*values)["num_loops"].int_value = bench->num_loops(); delete bench; return true; } bool runThreadedTest(thread_arg_t args[], int num_threads, int run_time) { pthread_t threads[num_threads]; volatile bool run = false; int rc; for (int i = 0; i < num_threads; i++) { args[i].run = &run; rc = pthread_create(&threads[i], NULL, runBandwidthThread, (void*)&args[i]); if (rc != 0) { printf("Failed to launch thread %d\n", i); return false; } } // Kick start the threads. run = true; // Let the threads run. sleep(run_time); // Stop the threads. run = false; // Wait for the threads to complete. for (int i = 0; i < num_threads; i++) { rc = pthread_join(threads[i], NULL); if (rc != 0) { printf("Thread %d failed to join.\n", i); return false; } printf("Thread %d: bandwidth using %s %0.2f MB/s\n", i, args[i].bench->getName(), args[i].avg_mb); } return true; } int per_core_bandwidth(int argc, char** argv) { arg_t values; if (!processThreadArgs(argc, argv, per_core_opts, &values)) { return -1; } std::vector<int> cpu_list; if (!getAvailCpus(&cpu_list)) { printf("Failed to get available cpu list.\n"); return -1; } thread_arg_t args[cpu_list.size()]; int i = 0; for (std::vector<int>::iterator it = cpu_list.begin(); it != cpu_list.end(); ++it, ++i) { args[i].core = *it; args[i].bench = createBandwidthBenchmarkObject(values); if (!args[i].bench) { return -1; } } printf("Running on %d cores\n", cpu_list.size()); printf(" run_time = %ds\n", values["run_time"].int_value); printf(" size = %d\n", values["size"].int_value); printf(" num_warm_loops = %d\n", values["num_warm_loops"].int_value); printf(" num_loops = %d\n", values["num_loops"].int_value); printf("\n"); if (!runThreadedTest(args, cpu_list.size(), values["run_time"].int_value)) { return -1; } return 0; } int multithread_bandwidth(int argc, char** argv) { arg_t values; if (!processThreadArgs(argc, argv, multithread_opts, &values)) { return -1; } if (values.count("num_threads") == 0) { printf("Must specify the num_threads value.\n"); return -1; } int num_threads = values["num_threads"].int_value; thread_arg_t args[num_threads]; for (int i = 0; i < num_threads; i++) { args[i].core = -1; args[i].bench = createBandwidthBenchmarkObject(values); if (!args[i].bench) { return -1; } } printf("Running %d threads\n", num_threads); printf(" run_time = %ds\n", values["run_time"].int_value); printf(" size = %d\n", values["size"].int_value); printf(" num_warm_loops = %d\n", values["num_warm_loops"].int_value); printf(" num_loops = %d\n", values["num_loops"].int_value); printf("\n"); if (!runThreadedTest(args, num_threads, values["run_time"].int_value)) { return -1; } return 0; } bool run_bandwidth_benchmark(int argc, char** argv, const char *name, std::vector<BandwidthBenchmark*> bench_objs) { arg_t values; values["size"].int_value = 0; values["num_warm_loops"].int_value = 0; values["num_loops"].int_value = 0; if (!processBandwidthOptions(argc, argv, bandwidth_opts, &values)) { return false; } size_t size = values["size"].int_value; if ((size % 64) != 0) { printf("The size value must be a multiple of 64.\n"); return false; } if (setpriority(PRIO_PROCESS, 0, -20)) { perror("Unable to raise priority of process."); return false; } bool preamble_printed = false; size_t num_warm_loops = values["num_warm_loops"].int_value; size_t num_loops = values["num_loops"].int_value; for (std::vector<BandwidthBenchmark*>::iterator it = bench_objs.begin(); it != bench_objs.end(); ++it) { if (!(*it)->canRun()) { continue; } if (!(*it)->setSize(values["size"].int_value)) { printf("Failed creating buffer for bandwidth test.\n"); return false; } if (num_warm_loops) { (*it)->set_num_warm_loops(num_warm_loops); } if (num_loops) { (*it)->set_num_loops(num_loops); } if (!preamble_printed) { preamble_printed = true; printf("Benchmarking %s bandwidth\n", name); printf(" size = %d\n", (*it)->size()); printf(" num_warm_loops = %d\n", (*it)->num_warm_loops()); printf(" num_loops = %d\n\n", (*it)->num_loops()); } (*it)->run(); printf(" %s bandwidth with %s: %0.2f MB/s\n", name, (*it)->getName(), (*it)->mb_per_sec()); } return true; } int copy_bandwidth(int argc, char** argv) { std::vector<BandwidthBenchmark*> bench_objs; bench_objs.push_back(new CopyLdrdStrdBenchmark()); bench_objs.push_back(new CopyLdmiaStmiaBenchmark()); bench_objs.push_back(new CopyVld1Vst1Benchmark()); bench_objs.push_back(new CopyVldrVstrBenchmark()); bench_objs.push_back(new CopyVldmiaVstmiaBenchmark()); bench_objs.push_back(new MemcpyBenchmark()); if (!run_bandwidth_benchmark(argc, argv, "copy", bench_objs)) { return -1; } return 0; } int write_bandwidth(int argc, char** argv) { std::vector<BandwidthBenchmark*> bench_objs; bench_objs.push_back(new WriteStrdBenchmark()); bench_objs.push_back(new WriteStmiaBenchmark()); bench_objs.push_back(new WriteVst1Benchmark()); bench_objs.push_back(new WriteVstrBenchmark()); bench_objs.push_back(new WriteVstmiaBenchmark()); bench_objs.push_back(new MemsetBenchmark()); if (!run_bandwidth_benchmark(argc, argv, "write", bench_objs)) { return -1; } return 0; } int read_bandwidth(int argc, char** argv) { std::vector<BandwidthBenchmark*> bench_objs; bench_objs.push_back(new ReadLdrdBenchmark()); bench_objs.push_back(new ReadLdmiaBenchmark()); bench_objs.push_back(new ReadVld1Benchmark()); bench_objs.push_back(new ReadVldrBenchmark()); bench_objs.push_back(new ReadVldmiaBenchmark()); if (!run_bandwidth_benchmark(argc, argv, "read", bench_objs)) { return -1; } return 0; }