1 /* 2 * Copyright (C) 2016 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 #include "PerfTest.h" 18 #include <fstream> 19 #include <iomanip> 20 #include <iostream> 21 #include <string> 22 23 #ifdef ASSERT 24 #undef ASSERT 25 #endif 26 #define ASSERT(cond) \ 27 do { \ 28 if (!(cond)) { \ 29 cerr << __func__ << ":" << __LINE__ << " condition:" << #cond << " failed\n" << endl; \ 30 exit(EXIT_FAILURE); \ 31 } \ 32 } while (0) 33 34 // the ratio that the service is synced on the same cpu beyond 35 // GOOD_SYNC_MIN is considered as good 36 #define GOOD_SYNC_MIN (0.6) 37 38 // the precision used for cout to dump float 39 #define DUMP_PRICISION 2 40 41 using std::cerr; 42 using std::cout; 43 using std::endl; 44 using std::left; 45 using std::ios; 46 using std::min; 47 using std::max; 48 using std::to_string; 49 using std::setprecision; 50 using std::setw; 51 using std::ofstream; 52 using std::make_tuple; 53 54 tuple<Pipe, Pipe> Pipe::createPipePair() { 55 int a[2]; 56 int b[2]; 57 58 int error1 = pipe(a); 59 int error2 = pipe(b); 60 ASSERT(error1 >= 0); 61 ASSERT(error2 >= 0); 62 63 return make_tuple(Pipe(a[0], b[1]), Pipe(b[0], a[1])); 64 } 65 66 Pipe::Pipe(Pipe&& rval) noexcept { 67 fd_read_ = rval.fd_read_; 68 fd_write_ = rval.fd_write_; 69 rval.fd_read_ = 0; 70 rval.fd_write_ = 0; 71 } 72 73 Pipe::~Pipe() { 74 if (fd_read_) { 75 close(fd_read_); 76 } 77 if (fd_write_) { 78 close(fd_write_); 79 } 80 } 81 82 Results Results::combine(const Results& a, const Results& b) { 83 Results ret; 84 for (uint32_t i = 0; i < kNumBuckets; i++) { 85 ret.buckets_[i] = a.buckets_[i] + b.buckets_[i]; 86 } 87 ret.worst_ = max(a.worst_, b.worst_); 88 ret.best_ = min(a.best_, b.best_); 89 ret.transactions_ = a.transactions_ + b.transactions_; 90 ret.miss_ = a.miss_ + b.miss_; 91 ret.total_time_ = a.total_time_ + b.total_time_; 92 return ret; 93 } 94 95 static void traceStop() { 96 ofstream file; 97 file.open(TRACE_PATH "/tracing_on", ios::out | ios::trunc); 98 file << '0' << endl; 99 file.close(); 100 } 101 102 void Results::addTime(uint64_t nano) { 103 buckets_[min(nano, kMaxTimeBucket - 1) / kTimePerBucket] += 1; 104 best_ = min(nano, best_); 105 worst_ = max(nano, worst_); 106 if (raw_dump_) { 107 raw_data_->push_back(nano); 108 } 109 transactions_ += 1; 110 total_time_ += nano; 111 if (missDeadline(nano)) { 112 miss_++; 113 if (tracing_) { 114 // There might be multiple process pair running the test concurrently 115 // each may execute following statements and only the first one actually 116 // stop the trace and any traceStop() after then has no effect. 117 traceStop(); 118 cerr << endl; 119 cerr << "deadline triggered: halt & stop trace" << endl; 120 cerr << "log:" TRACE_PATH "/trace" << endl; 121 cerr << endl; 122 exit(EXIT_FAILURE); 123 } 124 } 125 } 126 127 void Results::setupRawData() { 128 raw_dump_ = true; 129 if (raw_data_ == nullptr) { 130 raw_data_ = new list<uint64_t>; 131 } else { 132 raw_data_->clear(); 133 } 134 } 135 136 void Results::flushRawData() { 137 if (raw_dump_) { 138 bool first = true; 139 cout << "["; 140 for (auto nano : *raw_data_) { 141 cout << (first ? "" : ",") << to_string(nano); 142 first = false; 143 } 144 cout << "]," << endl; 145 delete raw_data_; 146 raw_data_ = nullptr; 147 } 148 } 149 150 void Results::dump() const { 151 double best = (double)best_ / 1.0E6; 152 double worst = (double)worst_ / 1.0E6; 153 double average = (double)total_time_ / transactions_ / 1.0E6; 154 int W = DUMP_PRICISION + 2; 155 cout << std::setprecision(DUMP_PRICISION) << "{ \"avg\":" << setw(W) << left << average 156 << ", \"wst\":" << setw(W) << left << worst << ", \"bst\":" << setw(W) << left << best 157 << ", \"miss\":" << left << miss_ << ", \"meetR\":" << setprecision(DUMP_PRICISION + 3) 158 << left << (1.0 - (double)miss_ / transactions_) << "}"; 159 } 160 161 void Results::dumpDistribution() const { 162 uint64_t cur_total = 0; 163 cout << "{ "; 164 cout << std::setprecision(DUMP_PRICISION + 3); 165 for (uint32_t i = 0; i < kNumBuckets; i++) { 166 float cur_time = kTimePerBucketMS * i + 0.5f * kTimePerBucketMS; 167 float accumulation = cur_total + buckets_[i]; 168 if ((cur_total < 0.5f * transactions_) && (accumulation >= 0.5f * transactions_)) { 169 cout << "\"p50\":" << cur_time << ", "; 170 } 171 if ((cur_total < 0.9f * transactions_) && (accumulation >= 0.9f * transactions_)) { 172 cout << "\"p90\":" << cur_time << ", "; 173 } 174 if ((cur_total < 0.95f * transactions_) && (accumulation >= 0.95f * transactions_)) { 175 cout << "\"p95\":" << cur_time << ", "; 176 } 177 if ((cur_total < 0.99f * transactions_) && (accumulation >= 0.99f * transactions_)) { 178 cout << "\"p99\": " << cur_time; 179 } 180 cur_total += buckets_[i]; 181 } 182 cout << "}"; 183 } 184 185 PResults PResults::combine(const PResults& a, const PResults& b) { 186 PResults ret; 187 ret.nNotInherent = a.nNotInherent + b.nNotInherent; 188 ret.nNotSync = a.nNotSync + b.nNotSync; 189 ret.other = Results::combine(a.other, b.other); 190 ret.fifo = Results::combine(a.fifo, b.fifo); 191 return ret; 192 } 193 194 void PResults::dump() const { 195 int no_trans = other.getTransactions() + fifo.getTransactions(); 196 double sync_ratio = (1.0 - (double)nNotSync / no_trans); 197 cout << "{\"SYNC\":\"" << ((sync_ratio > GOOD_SYNC_MIN) ? "GOOD" : "POOR") << "\"," 198 << "\"S\":" << (no_trans - nNotSync) << ",\"I\":" << no_trans << "," 199 << "\"R\":" << sync_ratio << "," << endl; 200 cout << " \"other_ms\":"; 201 other.dump(); 202 cout << "," << endl; 203 cout << " \"fifo_ms\": "; 204 fifo.dump(); 205 cout << "," << endl; 206 cout << " \"otherdis\":"; 207 other.dumpDistribution(); 208 cout << "," << endl; 209 cout << " \"fifodis\": "; 210 fifo.dumpDistribution(); 211 cout << endl; 212 cout << "}," << endl; 213 } 214