1 /* 2 * 3 * Copyright 2017 gRPC authors. 4 * 5 * Licensed under the Apache License, Version 2.0 (the "License"); 6 * you may not use this file except in compliance with the License. 7 * You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 * 17 */ 18 19 /* Test various closure related operations */ 20 21 #include <benchmark/benchmark.h> 22 #include <grpc/grpc.h> 23 #include <sstream> 24 25 #include "src/core/lib/gpr/spinlock.h" 26 #include "src/core/lib/iomgr/closure.h" 27 #include "src/core/lib/iomgr/combiner.h" 28 #include "src/core/lib/iomgr/exec_ctx.h" 29 30 #include "test/cpp/microbenchmarks/helpers.h" 31 #include "test/cpp/util/test_config.h" 32 33 auto& force_library_initialization = Library::get(); 34 35 static void BM_NoOpExecCtx(benchmark::State& state) { 36 TrackCounters track_counters; 37 while (state.KeepRunning()) { 38 grpc_core::ExecCtx exec_ctx; 39 } 40 track_counters.Finish(state); 41 } 42 BENCHMARK(BM_NoOpExecCtx); 43 44 static void BM_WellFlushed(benchmark::State& state) { 45 TrackCounters track_counters; 46 grpc_core::ExecCtx exec_ctx; 47 while (state.KeepRunning()) { 48 grpc_core::ExecCtx::Get()->Flush(); 49 } 50 51 track_counters.Finish(state); 52 } 53 BENCHMARK(BM_WellFlushed); 54 55 static void DoNothing(void* arg, grpc_error* error) {} 56 57 static void BM_ClosureInitAgainstExecCtx(benchmark::State& state) { 58 TrackCounters track_counters; 59 grpc_closure c; 60 while (state.KeepRunning()) { 61 benchmark::DoNotOptimize( 62 GRPC_CLOSURE_INIT(&c, DoNothing, nullptr, grpc_schedule_on_exec_ctx)); 63 } 64 track_counters.Finish(state); 65 } 66 BENCHMARK(BM_ClosureInitAgainstExecCtx); 67 68 static void BM_ClosureInitAgainstCombiner(benchmark::State& state) { 69 TrackCounters track_counters; 70 grpc_combiner* combiner = grpc_combiner_create(); 71 grpc_closure c; 72 grpc_core::ExecCtx exec_ctx; 73 while (state.KeepRunning()) { 74 benchmark::DoNotOptimize(GRPC_CLOSURE_INIT( 75 &c, DoNothing, nullptr, grpc_combiner_scheduler(combiner))); 76 } 77 GRPC_COMBINER_UNREF(combiner, "finished"); 78 79 track_counters.Finish(state); 80 } 81 BENCHMARK(BM_ClosureInitAgainstCombiner); 82 83 static void BM_ClosureRunOnExecCtx(benchmark::State& state) { 84 TrackCounters track_counters; 85 grpc_closure c; 86 GRPC_CLOSURE_INIT(&c, DoNothing, nullptr, grpc_schedule_on_exec_ctx); 87 grpc_core::ExecCtx exec_ctx; 88 while (state.KeepRunning()) { 89 GRPC_CLOSURE_RUN(&c, GRPC_ERROR_NONE); 90 grpc_core::ExecCtx::Get()->Flush(); 91 } 92 93 track_counters.Finish(state); 94 } 95 BENCHMARK(BM_ClosureRunOnExecCtx); 96 97 static void BM_ClosureCreateAndRun(benchmark::State& state) { 98 TrackCounters track_counters; 99 grpc_core::ExecCtx exec_ctx; 100 while (state.KeepRunning()) { 101 GRPC_CLOSURE_RUN( 102 GRPC_CLOSURE_CREATE(DoNothing, nullptr, grpc_schedule_on_exec_ctx), 103 GRPC_ERROR_NONE); 104 } 105 106 track_counters.Finish(state); 107 } 108 BENCHMARK(BM_ClosureCreateAndRun); 109 110 static void BM_ClosureInitAndRun(benchmark::State& state) { 111 TrackCounters track_counters; 112 grpc_core::ExecCtx exec_ctx; 113 grpc_closure c; 114 while (state.KeepRunning()) { 115 GRPC_CLOSURE_RUN( 116 GRPC_CLOSURE_INIT(&c, DoNothing, nullptr, grpc_schedule_on_exec_ctx), 117 GRPC_ERROR_NONE); 118 } 119 120 track_counters.Finish(state); 121 } 122 BENCHMARK(BM_ClosureInitAndRun); 123 124 static void BM_ClosureSchedOnExecCtx(benchmark::State& state) { 125 TrackCounters track_counters; 126 grpc_closure c; 127 GRPC_CLOSURE_INIT(&c, DoNothing, nullptr, grpc_schedule_on_exec_ctx); 128 grpc_core::ExecCtx exec_ctx; 129 while (state.KeepRunning()) { 130 GRPC_CLOSURE_SCHED(&c, GRPC_ERROR_NONE); 131 grpc_core::ExecCtx::Get()->Flush(); 132 } 133 134 track_counters.Finish(state); 135 } 136 BENCHMARK(BM_ClosureSchedOnExecCtx); 137 138 static void BM_ClosureSched2OnExecCtx(benchmark::State& state) { 139 TrackCounters track_counters; 140 grpc_closure c1; 141 grpc_closure c2; 142 GRPC_CLOSURE_INIT(&c1, DoNothing, nullptr, grpc_schedule_on_exec_ctx); 143 GRPC_CLOSURE_INIT(&c2, DoNothing, nullptr, grpc_schedule_on_exec_ctx); 144 grpc_core::ExecCtx exec_ctx; 145 while (state.KeepRunning()) { 146 GRPC_CLOSURE_SCHED(&c1, GRPC_ERROR_NONE); 147 GRPC_CLOSURE_SCHED(&c2, GRPC_ERROR_NONE); 148 grpc_core::ExecCtx::Get()->Flush(); 149 } 150 151 track_counters.Finish(state); 152 } 153 BENCHMARK(BM_ClosureSched2OnExecCtx); 154 155 static void BM_ClosureSched3OnExecCtx(benchmark::State& state) { 156 TrackCounters track_counters; 157 grpc_closure c1; 158 grpc_closure c2; 159 grpc_closure c3; 160 GRPC_CLOSURE_INIT(&c1, DoNothing, nullptr, grpc_schedule_on_exec_ctx); 161 GRPC_CLOSURE_INIT(&c2, DoNothing, nullptr, grpc_schedule_on_exec_ctx); 162 GRPC_CLOSURE_INIT(&c3, DoNothing, nullptr, grpc_schedule_on_exec_ctx); 163 grpc_core::ExecCtx exec_ctx; 164 while (state.KeepRunning()) { 165 GRPC_CLOSURE_SCHED(&c1, GRPC_ERROR_NONE); 166 GRPC_CLOSURE_SCHED(&c2, GRPC_ERROR_NONE); 167 GRPC_CLOSURE_SCHED(&c3, GRPC_ERROR_NONE); 168 grpc_core::ExecCtx::Get()->Flush(); 169 } 170 171 track_counters.Finish(state); 172 } 173 BENCHMARK(BM_ClosureSched3OnExecCtx); 174 175 static void BM_AcquireMutex(benchmark::State& state) { 176 TrackCounters track_counters; 177 // for comparison with the combiner stuff below 178 gpr_mu mu; 179 gpr_mu_init(&mu); 180 grpc_core::ExecCtx exec_ctx; 181 while (state.KeepRunning()) { 182 gpr_mu_lock(&mu); 183 DoNothing(nullptr, GRPC_ERROR_NONE); 184 gpr_mu_unlock(&mu); 185 } 186 187 track_counters.Finish(state); 188 } 189 BENCHMARK(BM_AcquireMutex); 190 191 static void BM_TryAcquireMutex(benchmark::State& state) { 192 TrackCounters track_counters; 193 // for comparison with the combiner stuff below 194 gpr_mu mu; 195 gpr_mu_init(&mu); 196 grpc_core::ExecCtx exec_ctx; 197 while (state.KeepRunning()) { 198 if (gpr_mu_trylock(&mu)) { 199 DoNothing(nullptr, GRPC_ERROR_NONE); 200 gpr_mu_unlock(&mu); 201 } else { 202 abort(); 203 } 204 } 205 206 track_counters.Finish(state); 207 } 208 BENCHMARK(BM_TryAcquireMutex); 209 210 static void BM_AcquireSpinlock(benchmark::State& state) { 211 TrackCounters track_counters; 212 // for comparison with the combiner stuff below 213 gpr_spinlock mu = GPR_SPINLOCK_INITIALIZER; 214 grpc_core::ExecCtx exec_ctx; 215 while (state.KeepRunning()) { 216 gpr_spinlock_lock(&mu); 217 DoNothing(nullptr, GRPC_ERROR_NONE); 218 gpr_spinlock_unlock(&mu); 219 } 220 221 track_counters.Finish(state); 222 } 223 BENCHMARK(BM_AcquireSpinlock); 224 225 static void BM_TryAcquireSpinlock(benchmark::State& state) { 226 TrackCounters track_counters; 227 // for comparison with the combiner stuff below 228 gpr_spinlock mu = GPR_SPINLOCK_INITIALIZER; 229 grpc_core::ExecCtx exec_ctx; 230 while (state.KeepRunning()) { 231 if (gpr_spinlock_trylock(&mu)) { 232 DoNothing(nullptr, GRPC_ERROR_NONE); 233 gpr_spinlock_unlock(&mu); 234 } else { 235 abort(); 236 } 237 } 238 239 track_counters.Finish(state); 240 } 241 BENCHMARK(BM_TryAcquireSpinlock); 242 243 static void BM_ClosureSchedOnCombiner(benchmark::State& state) { 244 TrackCounters track_counters; 245 grpc_combiner* combiner = grpc_combiner_create(); 246 grpc_closure c; 247 GRPC_CLOSURE_INIT(&c, DoNothing, nullptr, grpc_combiner_scheduler(combiner)); 248 grpc_core::ExecCtx exec_ctx; 249 while (state.KeepRunning()) { 250 GRPC_CLOSURE_SCHED(&c, GRPC_ERROR_NONE); 251 grpc_core::ExecCtx::Get()->Flush(); 252 } 253 GRPC_COMBINER_UNREF(combiner, "finished"); 254 255 track_counters.Finish(state); 256 } 257 BENCHMARK(BM_ClosureSchedOnCombiner); 258 259 static void BM_ClosureSched2OnCombiner(benchmark::State& state) { 260 TrackCounters track_counters; 261 grpc_combiner* combiner = grpc_combiner_create(); 262 grpc_closure c1; 263 grpc_closure c2; 264 GRPC_CLOSURE_INIT(&c1, DoNothing, nullptr, grpc_combiner_scheduler(combiner)); 265 GRPC_CLOSURE_INIT(&c2, DoNothing, nullptr, grpc_combiner_scheduler(combiner)); 266 grpc_core::ExecCtx exec_ctx; 267 while (state.KeepRunning()) { 268 GRPC_CLOSURE_SCHED(&c1, GRPC_ERROR_NONE); 269 GRPC_CLOSURE_SCHED(&c2, GRPC_ERROR_NONE); 270 grpc_core::ExecCtx::Get()->Flush(); 271 } 272 GRPC_COMBINER_UNREF(combiner, "finished"); 273 274 track_counters.Finish(state); 275 } 276 BENCHMARK(BM_ClosureSched2OnCombiner); 277 278 static void BM_ClosureSched3OnCombiner(benchmark::State& state) { 279 TrackCounters track_counters; 280 grpc_combiner* combiner = grpc_combiner_create(); 281 grpc_closure c1; 282 grpc_closure c2; 283 grpc_closure c3; 284 GRPC_CLOSURE_INIT(&c1, DoNothing, nullptr, grpc_combiner_scheduler(combiner)); 285 GRPC_CLOSURE_INIT(&c2, DoNothing, nullptr, grpc_combiner_scheduler(combiner)); 286 GRPC_CLOSURE_INIT(&c3, DoNothing, nullptr, grpc_combiner_scheduler(combiner)); 287 grpc_core::ExecCtx exec_ctx; 288 while (state.KeepRunning()) { 289 GRPC_CLOSURE_SCHED(&c1, GRPC_ERROR_NONE); 290 GRPC_CLOSURE_SCHED(&c2, GRPC_ERROR_NONE); 291 GRPC_CLOSURE_SCHED(&c3, GRPC_ERROR_NONE); 292 grpc_core::ExecCtx::Get()->Flush(); 293 } 294 GRPC_COMBINER_UNREF(combiner, "finished"); 295 296 track_counters.Finish(state); 297 } 298 BENCHMARK(BM_ClosureSched3OnCombiner); 299 300 static void BM_ClosureSched2OnTwoCombiners(benchmark::State& state) { 301 TrackCounters track_counters; 302 grpc_combiner* combiner1 = grpc_combiner_create(); 303 grpc_combiner* combiner2 = grpc_combiner_create(); 304 grpc_closure c1; 305 grpc_closure c2; 306 GRPC_CLOSURE_INIT(&c1, DoNothing, nullptr, 307 grpc_combiner_scheduler(combiner1)); 308 GRPC_CLOSURE_INIT(&c2, DoNothing, nullptr, 309 grpc_combiner_scheduler(combiner2)); 310 grpc_core::ExecCtx exec_ctx; 311 while (state.KeepRunning()) { 312 GRPC_CLOSURE_SCHED(&c1, GRPC_ERROR_NONE); 313 GRPC_CLOSURE_SCHED(&c2, GRPC_ERROR_NONE); 314 grpc_core::ExecCtx::Get()->Flush(); 315 } 316 GRPC_COMBINER_UNREF(combiner1, "finished"); 317 GRPC_COMBINER_UNREF(combiner2, "finished"); 318 319 track_counters.Finish(state); 320 } 321 BENCHMARK(BM_ClosureSched2OnTwoCombiners); 322 323 static void BM_ClosureSched4OnTwoCombiners(benchmark::State& state) { 324 TrackCounters track_counters; 325 grpc_combiner* combiner1 = grpc_combiner_create(); 326 grpc_combiner* combiner2 = grpc_combiner_create(); 327 grpc_closure c1; 328 grpc_closure c2; 329 grpc_closure c3; 330 grpc_closure c4; 331 GRPC_CLOSURE_INIT(&c1, DoNothing, nullptr, 332 grpc_combiner_scheduler(combiner1)); 333 GRPC_CLOSURE_INIT(&c2, DoNothing, nullptr, 334 grpc_combiner_scheduler(combiner2)); 335 GRPC_CLOSURE_INIT(&c3, DoNothing, nullptr, 336 grpc_combiner_scheduler(combiner1)); 337 GRPC_CLOSURE_INIT(&c4, DoNothing, nullptr, 338 grpc_combiner_scheduler(combiner2)); 339 grpc_core::ExecCtx exec_ctx; 340 while (state.KeepRunning()) { 341 GRPC_CLOSURE_SCHED(&c1, GRPC_ERROR_NONE); 342 GRPC_CLOSURE_SCHED(&c2, GRPC_ERROR_NONE); 343 GRPC_CLOSURE_SCHED(&c3, GRPC_ERROR_NONE); 344 GRPC_CLOSURE_SCHED(&c4, GRPC_ERROR_NONE); 345 grpc_core::ExecCtx::Get()->Flush(); 346 } 347 GRPC_COMBINER_UNREF(combiner1, "finished"); 348 GRPC_COMBINER_UNREF(combiner2, "finished"); 349 350 track_counters.Finish(state); 351 } 352 BENCHMARK(BM_ClosureSched4OnTwoCombiners); 353 354 // Helper that continuously reschedules the same closure against something until 355 // the benchmark is complete 356 class Rescheduler { 357 public: 358 Rescheduler(benchmark::State& state, grpc_closure_scheduler* scheduler) 359 : state_(state) { 360 GRPC_CLOSURE_INIT(&closure_, Step, this, scheduler); 361 } 362 363 void ScheduleFirst() { GRPC_CLOSURE_SCHED(&closure_, GRPC_ERROR_NONE); } 364 365 void ScheduleFirstAgainstDifferentScheduler( 366 grpc_closure_scheduler* scheduler) { 367 GRPC_CLOSURE_SCHED(GRPC_CLOSURE_CREATE(Step, this, scheduler), 368 GRPC_ERROR_NONE); 369 } 370 371 private: 372 benchmark::State& state_; 373 grpc_closure closure_; 374 375 static void Step(void* arg, grpc_error* error) { 376 Rescheduler* self = static_cast<Rescheduler*>(arg); 377 if (self->state_.KeepRunning()) { 378 GRPC_CLOSURE_SCHED(&self->closure_, GRPC_ERROR_NONE); 379 } 380 } 381 }; 382 383 static void BM_ClosureReschedOnExecCtx(benchmark::State& state) { 384 TrackCounters track_counters; 385 grpc_core::ExecCtx exec_ctx; 386 Rescheduler r(state, grpc_schedule_on_exec_ctx); 387 r.ScheduleFirst(); 388 grpc_core::ExecCtx::Get()->Flush(); 389 track_counters.Finish(state); 390 } 391 BENCHMARK(BM_ClosureReschedOnExecCtx); 392 393 static void BM_ClosureReschedOnCombiner(benchmark::State& state) { 394 TrackCounters track_counters; 395 grpc_core::ExecCtx exec_ctx; 396 grpc_combiner* combiner = grpc_combiner_create(); 397 Rescheduler r(state, grpc_combiner_scheduler(combiner)); 398 r.ScheduleFirst(); 399 grpc_core::ExecCtx::Get()->Flush(); 400 GRPC_COMBINER_UNREF(combiner, "finished"); 401 402 track_counters.Finish(state); 403 } 404 BENCHMARK(BM_ClosureReschedOnCombiner); 405 406 static void BM_ClosureReschedOnCombinerFinally(benchmark::State& state) { 407 TrackCounters track_counters; 408 grpc_core::ExecCtx exec_ctx; 409 grpc_combiner* combiner = grpc_combiner_create(); 410 Rescheduler r(state, grpc_combiner_finally_scheduler(combiner)); 411 r.ScheduleFirstAgainstDifferentScheduler(grpc_combiner_scheduler(combiner)); 412 grpc_core::ExecCtx::Get()->Flush(); 413 GRPC_COMBINER_UNREF(combiner, "finished"); 414 415 track_counters.Finish(state); 416 } 417 BENCHMARK(BM_ClosureReschedOnCombinerFinally); 418 419 // Some distros have RunSpecifiedBenchmarks under the benchmark namespace, 420 // and others do not. This allows us to support both modes. 421 namespace benchmark { 422 void RunTheBenchmarksNamespaced() { RunSpecifiedBenchmarks(); } 423 } // namespace benchmark 424 425 int main(int argc, char** argv) { 426 ::benchmark::Initialize(&argc, argv); 427 ::grpc::testing::InitTest(&argc, &argv, false); 428 benchmark::RunTheBenchmarksNamespaced(); 429 return 0; 430 } 431