| /* |
| * Copyright (C) 2016 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 "PerfTest.h" |
| #include <fstream> |
| #include <iomanip> |
| #include <iostream> |
| #include <string> |
| |
| #ifdef ASSERT |
| #undef ASSERT |
| #endif |
| #define ASSERT(cond) \ |
| do { \ |
| if (!(cond)) { \ |
| cerr << __func__ << ":" << __LINE__ << " condition:" << #cond << " failed\n" << endl; \ |
| exit(EXIT_FAILURE); \ |
| } \ |
| } while (0) |
| |
| // the ratio that the service is synced on the same cpu beyond |
| // GOOD_SYNC_MIN is considered as good |
| #define GOOD_SYNC_MIN (0.6) |
| |
| // the precision used for cout to dump float |
| #define DUMP_PRICISION 2 |
| |
| using std::cerr; |
| using std::cout; |
| using std::endl; |
| using std::left; |
| using std::ios; |
| using std::min; |
| using std::max; |
| using std::to_string; |
| using std::setprecision; |
| using std::setw; |
| using std::ofstream; |
| using std::make_tuple; |
| |
| tuple<Pipe, Pipe> Pipe::createPipePair() { |
| int a[2]; |
| int b[2]; |
| |
| int error1 = pipe(a); |
| int error2 = pipe(b); |
| ASSERT(error1 >= 0); |
| ASSERT(error2 >= 0); |
| |
| return make_tuple(Pipe(a[0], b[1]), Pipe(b[0], a[1])); |
| } |
| |
| Pipe::Pipe(Pipe&& rval) noexcept { |
| fd_read_ = rval.fd_read_; |
| fd_write_ = rval.fd_write_; |
| rval.fd_read_ = 0; |
| rval.fd_write_ = 0; |
| } |
| |
| Pipe::~Pipe() { |
| if (fd_read_) { |
| close(fd_read_); |
| } |
| if (fd_write_) { |
| close(fd_write_); |
| } |
| } |
| |
| Results Results::combine(const Results& a, const Results& b) { |
| Results ret; |
| for (uint32_t i = 0; i < kNumBuckets; i++) { |
| ret.buckets_[i] = a.buckets_[i] + b.buckets_[i]; |
| } |
| ret.worst_ = max(a.worst_, b.worst_); |
| ret.best_ = min(a.best_, b.best_); |
| ret.transactions_ = a.transactions_ + b.transactions_; |
| ret.miss_ = a.miss_ + b.miss_; |
| ret.total_time_ = a.total_time_ + b.total_time_; |
| return ret; |
| } |
| |
| static void traceStop() { |
| ofstream file; |
| file.open(TRACE_PATH "/tracing_on", ios::out | ios::trunc); |
| file << '0' << endl; |
| file.close(); |
| } |
| |
| void Results::addTime(uint64_t nano) { |
| buckets_[min(nano, kMaxTimeBucket - 1) / kTimePerBucket] += 1; |
| best_ = min(nano, best_); |
| worst_ = max(nano, worst_); |
| if (raw_dump_) { |
| raw_data_->push_back(nano); |
| } |
| transactions_ += 1; |
| total_time_ += nano; |
| if (missDeadline(nano)) { |
| miss_++; |
| if (tracing_) { |
| // There might be multiple process pair running the test concurrently |
| // each may execute following statements and only the first one actually |
| // stop the trace and any traceStop() after then has no effect. |
| traceStop(); |
| cerr << endl; |
| cerr << "deadline triggered: halt & stop trace" << endl; |
| cerr << "log:" TRACE_PATH "/trace" << endl; |
| cerr << endl; |
| exit(EXIT_FAILURE); |
| } |
| } |
| } |
| |
| void Results::setupRawData() { |
| raw_dump_ = true; |
| if (raw_data_ == nullptr) { |
| raw_data_ = new list<uint64_t>; |
| } else { |
| raw_data_->clear(); |
| } |
| } |
| |
| void Results::flushRawData() { |
| if (raw_dump_) { |
| bool first = true; |
| cout << "["; |
| for (auto nano : *raw_data_) { |
| cout << (first ? "" : ",") << to_string(nano); |
| first = false; |
| } |
| cout << "]," << endl; |
| delete raw_data_; |
| raw_data_ = nullptr; |
| } |
| } |
| |
| void Results::dump() const { |
| double best = (double)best_ / 1.0E6; |
| double worst = (double)worst_ / 1.0E6; |
| double average = (double)total_time_ / transactions_ / 1.0E6; |
| int W = DUMP_PRICISION + 2; |
| cout << std::setprecision(DUMP_PRICISION) << "{ \"avg\":" << setw(W) << left << average |
| << ", \"wst\":" << setw(W) << left << worst << ", \"bst\":" << setw(W) << left << best |
| << ", \"miss\":" << left << miss_ << ", \"meetR\":" << setprecision(DUMP_PRICISION + 3) |
| << left << (1.0 - (double)miss_ / transactions_) << "}"; |
| } |
| |
| void Results::dumpDistribution() const { |
| uint64_t cur_total = 0; |
| cout << "{ "; |
| cout << std::setprecision(DUMP_PRICISION + 3); |
| for (uint32_t i = 0; i < kNumBuckets; i++) { |
| float cur_time = kTimePerBucketMS * i + 0.5f * kTimePerBucketMS; |
| float accumulation = cur_total + buckets_[i]; |
| if ((cur_total < 0.5f * transactions_) && (accumulation >= 0.5f * transactions_)) { |
| cout << "\"p50\":" << cur_time << ", "; |
| } |
| if ((cur_total < 0.9f * transactions_) && (accumulation >= 0.9f * transactions_)) { |
| cout << "\"p90\":" << cur_time << ", "; |
| } |
| if ((cur_total < 0.95f * transactions_) && (accumulation >= 0.95f * transactions_)) { |
| cout << "\"p95\":" << cur_time << ", "; |
| } |
| if ((cur_total < 0.99f * transactions_) && (accumulation >= 0.99f * transactions_)) { |
| cout << "\"p99\": " << cur_time; |
| } |
| cur_total += buckets_[i]; |
| } |
| cout << "}"; |
| } |
| |
| PResults PResults::combine(const PResults& a, const PResults& b) { |
| PResults ret; |
| ret.nNotInherent = a.nNotInherent + b.nNotInherent; |
| ret.nNotSync = a.nNotSync + b.nNotSync; |
| ret.other = Results::combine(a.other, b.other); |
| ret.fifo = Results::combine(a.fifo, b.fifo); |
| return ret; |
| } |
| |
| void PResults::dump() const { |
| int no_trans = other.getTransactions() + fifo.getTransactions(); |
| double sync_ratio = (1.0 - (double)nNotSync / no_trans); |
| cout << "{\"SYNC\":\"" << ((sync_ratio > GOOD_SYNC_MIN) ? "GOOD" : "POOR") << "\"," |
| << "\"S\":" << (no_trans - nNotSync) << ",\"I\":" << no_trans << "," |
| << "\"R\":" << sync_ratio << "," << endl; |
| cout << " \"other_ms\":"; |
| other.dump(); |
| cout << "," << endl; |
| cout << " \"fifo_ms\": "; |
| fifo.dump(); |
| cout << "," << endl; |
| cout << " \"otherdis\":"; |
| other.dumpDistribution(); |
| cout << "," << endl; |
| cout << " \"fifodis\": "; |
| fifo.dumpDistribution(); |
| cout << endl; |
| cout << "}," << endl; |
| } |