Commit b6641eb7 authored by Maged Michael's avatar Maged Michael Committed by Facebook Github Bot

RequestContext: Add microbenchmarks

Summary: Add microbenchmarks for request context operations.

Reviewed By: LeeHowes

Differential Revision: D17434204

fbshipit-source-id: 22c804d71b922dd6894bdf6d98449f0c60c83206
parent 08c318db
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* 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 <folly/container/Array.h>
#include <folly/io/async/test/RequestContextHelper.h>
#include <folly/portability/GFlags.h>
#include <folly/synchronization/test/Barrier.h>
#include <chrono>
#include <cmath>
#include <iomanip>
#include <iostream>
#include <thread>
#include <vector>
DEFINE_int32(reps, 10, "number of reps");
DEFINE_int32(ops, 1000000, "number of operations per rep");
using namespace folly;
RequestToken token("test");
template <typename Func>
inline uint64_t run_once(int nthr, const Func& fn) {
folly::test::Barrier b1(nthr + 1);
std::vector<std::thread> thr(nthr);
for (int tid = 0; tid < nthr; ++tid) {
thr[tid] = std::thread([&, tid] {
b1.wait();
fn(tid);
});
}
b1.wait();
/* begin time measurement */
auto const tbegin = std::chrono::steady_clock::now();
/* wait for completion */
for (int i = 0; i < nthr; ++i) {
thr[i].join();
}
/* end time measurement */
auto const tend = std::chrono::steady_clock::now();
auto const dur = tend - tbegin;
return std::chrono::duration_cast<std::chrono::nanoseconds>(dur).count();
}
template <typename Func>
uint64_t runBench(int ops, int nthr, const Func& fn) {
uint64_t reps = FLAGS_reps;
uint64_t min = UINTMAX_MAX;
uint64_t max = 0;
uint64_t sum = 0;
std::vector<uint64_t> durs(reps);
for (uint64_t r = 0; r < reps; ++r) {
uint64_t dur = run_once(nthr, fn);
durs[r] = dur;
sum += dur;
min = std::min(min, dur);
max = std::max(max, dur);
// if each rep takes too long run at least 3 reps
const uint64_t minute = 60000000000UL;
if (sum > minute && r >= 2) {
reps = r + 1;
break;
}
}
const std::string ns_unit = " ns";
uint64_t avg = sum / reps;
uint64_t res = min;
uint64_t varsum = 0;
for (uint64_t r = 0; r < reps; ++r) {
auto term = int64_t(reps * durs[r]) - int64_t(sum);
varsum += term * term;
}
uint64_t dev = uint64_t(std::sqrt(varsum) * std::pow(reps, -1.5));
std::cout << " " << std::setw(4) << max / ops << ns_unit;
std::cout << " " << std::setw(4) << avg / ops << ns_unit;
std::cout << " " << std::setw(4) << dev / ops << ns_unit;
std::cout << " " << std::setw(4) << res / ops << ns_unit;
std::cout << std::endl;
return res;
}
uint64_t bench_set_clearContextData(int nthr, uint64_t ops) {
auto fn = [&](int tid) {
RequestContextScopeGuard g;
for (uint64_t i = tid; i < ops; i += nthr) {
RequestContext::get()->setContextData(
token, std::make_unique<TestData>(tid));
RequestContext::get()->clearContextData(token);
}
};
return runBench(ops, nthr, fn);
}
uint64_t bench_hasContextData(int nthr, uint64_t ops, bool hit) {
auto fn = [&](int tid) {
RequestContextScopeGuard g;
if (hit) {
RequestContext::get()->setContextData(
token, std::make_unique<TestData>(tid));
}
for (uint64_t i = tid; i < ops; i += nthr) {
RequestContext::get()->hasContextData(token);
}
};
return runBench(ops, nthr, fn);
}
uint64_t bench_getContextData(int nthr, uint64_t ops, bool hit) {
auto fn = [&](int tid) {
RequestContextScopeGuard g;
if (hit) {
RequestContext::get()->setContextData(
token, std::make_unique<TestData>(tid));
}
for (uint64_t i = tid; i < ops; i += nthr) {
RequestContext::get()->getContextData(token);
}
};
return runBench(ops, nthr, fn);
}
uint64_t bench_onSet(int nthr, uint64_t ops, bool nonempty) {
auto fn = [&](int tid) {
RequestContextScopeGuard g;
if (nonempty) {
RequestContext::get()->setContextData(
token, std::make_unique<TestData>(tid));
}
for (uint64_t i = tid; i < ops; i += nthr) {
RequestContext::get()->onSet();
}
};
return runBench(ops, nthr, fn);
}
uint64_t bench_onUnset(int nthr, uint64_t ops, bool nonempty) {
auto fn = [&](int tid) {
RequestContextScopeGuard g;
if (nonempty) {
RequestContext::get()->setContextData(
token, std::make_unique<TestData>(tid));
}
for (uint64_t i = tid; i < ops; i += nthr) {
RequestContext::get()->onUnset();
}
};
return runBench(ops, nthr, fn);
}
uint64_t bench_setContext(int nthr, uint64_t ops, bool nonempty) {
auto fn = [&](int tid) {
auto ctx = std::make_shared<RequestContext>();
if (nonempty) {
ctx->setContextData(token, std::make_unique<TestData>(1));
}
RequestContext::setContext(std::move(ctx));
ctx = std::make_shared<RequestContext>();
if (nonempty) {
ctx->setContextData(token, std::make_unique<TestData>(2));
}
for (uint64_t i = tid; i < ops; i += nthr) {
ctx = RequestContext::setContext(std::move(ctx));
}
RequestContext::setContext(nullptr);
};
return runBench(ops, nthr, fn);
}
uint64_t bench_RequestContextScopeGuard(int nthr, uint64_t ops, bool nonempty) {
auto fn = [&](int tid) {
RequestContextScopeGuard g1;
if (nonempty) {
RequestContext::get()->setContextData(
token, std::make_unique<TestData>(1));
}
auto ctx = std::make_shared<RequestContext>();
if (nonempty) {
ctx->setContextData(token, std::make_unique<TestData>(2));
}
for (uint64_t i = tid; i < ops; i += nthr) {
RequestContextScopeGuard g2(ctx);
}
};
return runBench(ops, nthr, fn);
}
uint64_t bench_ShallowCopyRequestContextScopeGuard(
int nthr,
uint64_t ops,
bool nonempty) {
auto fn = [&](int tid) {
RequestContextScopeGuard g1;
if (nonempty) {
RequestContext::get()->setContextData(
token, std::make_unique<TestData>(1));
for (uint64_t i = tid; i < ops; i += nthr) {
ShallowCopyRequestContextScopeGuard g2(
token, std::make_unique<TestData>(2));
}
} else {
for (uint64_t i = tid; i < ops; i += nthr) {
ShallowCopyRequestContextScopeGuard g2;
}
}
};
return runBench(ops, nthr, fn);
}
void dottedLine() {
std::cout
<< "........................................................................"
<< std::endl;
}
void doubleLine() {
std::cout
<< "========================================================================"
<< std::endl;
}
constexpr auto nthr = folly::make_array<int>(1, 10);
void benches() {
doubleLine();
std::cout << std::setw(2) << FLAGS_reps << " reps of " << std::setw(8)
<< FLAGS_ops << " operations\n";
dottedLine();
std::cout << "$ numactl -N 1 $dir/request_context_benchmark\n";
doubleLine();
std::cout
<< "Test name Max time Avg time Dev time Min time"
<< std::endl;
for (int i : nthr) {
std::cout << "============================== " << std::setw(2) << i
<< " threads "
<< "==============================" << std::endl;
const uint64_t ops = FLAGS_ops;
std::cout << "set/clearContextData ";
bench_set_clearContextData(i, ops);
std::cout << "hasContextData-empty ";
bench_hasContextData(i, ops, false);
std::cout << "hasContextData-hit ";
bench_hasContextData(i, ops, true);
std::cout << "getContextData-empty ";
bench_getContextData(i, ops, false);
std::cout << "getContextData-hit ";
bench_getContextData(i, ops, true);
std::cout << "onSet-empty ";
bench_onSet(i, ops, false);
std::cout << "onSet-nonempty ";
bench_onSet(i, ops, true);
std::cout << "onUnset-empty ";
bench_onUnset(i, ops, false);
std::cout << "onUnset-nonempty ";
bench_onUnset(i, ops, true);
std::cout << "setContext-empty ";
bench_setContext(i, ops, false);
std::cout << "setContext-nonempty ";
bench_setContext(i, ops, true);
std::cout << "RequestContextScopeGuard-empty ";
bench_RequestContextScopeGuard(i, ops, false);
std::cout << "RequestContextScopeG...-nonempty";
bench_RequestContextScopeGuard(i, ops, true);
std::cout << "ShallowCopyRequestCon...-empty ";
bench_ShallowCopyRequestContextScopeGuard(i, ops, false);
std::cout << "ShallowCopyRequest...-nonempty ";
bench_ShallowCopyRequestContextScopeGuard(i, ops, true);
}
doubleLine();
}
int main(int argc, char** argv) {
gflags::ParseCommandLineFlags(&argc, &argv, true);
benches();
}
/*
========================================================================
10 reps of 1000000 operations
........................................................................
$ numactl -N 1 $dir/request_context_benchmark
========================================================================
Test name Max time Avg time Dev time Min time
============================== 1 threads ==============================
set/clearContextData 127 ns 116 ns 5 ns 105 ns
hasContextData-empty 23 ns 21 ns 0 ns 20 ns
hasContextData-hit 30 ns 28 ns 1 ns 27 ns
getContextData-empty 24 ns 22 ns 0 ns 22 ns
getContextData-hit 32 ns 30 ns 1 ns 28 ns
onSet-empty 22 ns 21 ns 0 ns 20 ns
onSet-nonempty 24 ns 22 ns 0 ns 21 ns
onUnset-empty 22 ns 21 ns 0 ns 21 ns
onUnset-nonempty 23 ns 23 ns 0 ns 21 ns
setContext-empty 72 ns 69 ns 1 ns 67 ns
setContext-nonempty 79 ns 76 ns 2 ns 72 ns
RequestContextScopeGuard-empty 158 ns 149 ns 4 ns 143 ns
RequestContextScopeG...-nonempty 164 ns 158 ns 3 ns 152 ns
ShallowCopyRequestCon...-empty 127 ns 121 ns 2 ns 118 ns
ShallowCopyRequest...-nonempty 256 ns 240 ns 8 ns 231 ns
============================== 10 threads ==============================
set/clearContextData 11 ns 11 ns 0 ns 10 ns
hasContextData-empty 3 ns 2 ns 0 ns 2 ns
hasContextData-hit 3 ns 3 ns 0 ns 2 ns
getContextData-empty 3 ns 2 ns 0 ns 2 ns
getContextData-hit 4 ns 3 ns 0 ns 2 ns
onSet-empty 3 ns 3 ns 0 ns 2 ns
onSet-nonempty 3 ns 3 ns 0 ns 2 ns
onUnset-empty 3 ns 2 ns 0 ns 2 ns
onUnset-nonempty 3 ns 3 ns 0 ns 2 ns
setContext-empty 12 ns 8 ns 2 ns 7 ns
setContext-nonempty 14 ns 10 ns 2 ns 7 ns
RequestContextScopeGuard-empty 27 ns 19 ns 5 ns 15 ns
RequestContextScopeG...-nonempty 28 ns 20 ns 5 ns 16 ns
ShallowCopyRequestCon...-empty 21 ns 14 ns 3 ns 12 ns
ShallowCopyRequest...-nonempty 42 ns 30 ns 7 ns 25 ns
========================================================================
*/
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* 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.
*/
#pragma once
#include <folly/io/async/Request.h>
namespace folly {
class TestData : public RequestData {
public:
explicit TestData(int data) : data_(data) {}
~TestData() override {}
bool hasCallback() override {
return true;
}
void onSet() override {
set_++;
}
void onUnset() override {
unset_++;
}
int set_ = 0, unset_ = 0;
int data_;
};
} // namespace folly
...@@ -19,33 +19,13 @@ ...@@ -19,33 +19,13 @@
#include <folly/Memory.h> #include <folly/Memory.h>
#include <folly/io/async/EventBase.h> #include <folly/io/async/EventBase.h>
#include <folly/io/async/Request.h> #include <folly/io/async/Request.h>
#include <folly/io/async/test/RequestContextHelper.h>
#include <folly/portability/GTest.h> #include <folly/portability/GTest.h>
using namespace folly; using namespace folly;
RequestToken testtoken("test"); RequestToken testtoken("test");
class TestData : public RequestData {
public:
explicit TestData(int data) : data_(data) {}
~TestData() override {}
bool hasCallback() override {
return true;
}
void onSet() override {
set_++;
}
void onUnset() override {
unset_++;
}
int set_ = 0, unset_ = 0;
int data_;
};
class RequestContextTest : public ::testing::Test { class RequestContextTest : public ::testing::Test {
protected: protected:
void SetUp() override { void SetUp() override {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment