Line data Source code
1 : /*
2 : *
3 : * Copyright 2015, Google Inc.
4 : * All rights reserved.
5 : *
6 : * Redistribution and use in source and binary forms, with or without
7 : * modification, are permitted provided that the following conditions are
8 : * met:
9 : *
10 : * * Redistributions of source code must retain the above copyright
11 : * notice, this list of conditions and the following disclaimer.
12 : * * Redistributions in binary form must reproduce the above
13 : * copyright notice, this list of conditions and the following disclaimer
14 : * in the documentation and/or other materials provided with the
15 : * distribution.
16 : * * Neither the name of Google Inc. nor the names of its
17 : * contributors may be used to endorse or promote products derived from
18 : * this software without specific prior written permission.
19 : *
20 : * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21 : * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22 : * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23 : * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
24 : * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25 : * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26 : * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27 : * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28 : * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29 : * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30 : * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 : *
32 : */
33 :
34 : #ifndef TEST_QPS_INTERARRIVAL_H
35 : #define TEST_QPS_INTERARRIVAL_H
36 :
37 : #include <chrono>
38 : #include <cmath>
39 : #include <cstdlib>
40 : #include <vector>
41 :
42 : #include <grpc++/support/config.h>
43 :
44 : namespace grpc {
45 : namespace testing {
46 :
47 : // First create classes that define a random distribution
48 : // Note that this code does not include C++-specific random distribution
49 : // features supported in std::random. Although this would make this code easier,
50 : // this code is required to serve as the template code for other language
51 : // stacks. Thus, this code only uses a uniform distribution of doubles [0,1)
52 : // and then provides the distribution functions itself.
53 :
54 : class RandomDist {
55 : public:
56 1 : RandomDist() {}
57 : virtual ~RandomDist() = 0;
58 : // Argument to operator() is a uniform double in the range [0,1)
59 : virtual double operator()(double uni) const = 0;
60 : };
61 :
62 1 : inline RandomDist::~RandomDist() {}
63 :
64 : // ExpDist implements an exponential distribution, which is the
65 : // interarrival distribution for a Poisson process. The parameter
66 : // lambda is the mean rate of arrivals. This is the
67 : // most useful distribution since it is actually additive and
68 : // memoryless. It is a good representation of activity coming in from
69 : // independent identical stationary sources. For more information,
70 : // see http://en.wikipedia.org/wiki/Exponential_distribution
71 :
72 : class ExpDist GRPC_FINAL : public RandomDist {
73 : public:
74 1 : explicit ExpDist(double lambda) : lambda_recip_(1.0 / lambda) {}
75 2 : ~ExpDist() GRPC_OVERRIDE {}
76 1000000 : double operator()(double uni) const GRPC_OVERRIDE {
77 : // Note: Use 1.0-uni above to avoid NaN if uni is 0
78 1000000 : return lambda_recip_ * (-log(1.0 - uni));
79 : }
80 :
81 : private:
82 : double lambda_recip_;
83 : };
84 :
85 : // UniformDist implements a random distribution that has
86 : // interarrival time uniformly spread between [lo,hi). The
87 : // mean interarrival time is (lo+hi)/2. For more information,
88 : // see http://en.wikipedia.org/wiki/Uniform_distribution_%28continuous%29
89 :
90 : class UniformDist GRPC_FINAL : public RandomDist {
91 : public:
92 0 : UniformDist(double lo, double hi) : lo_(lo), range_(hi - lo) {}
93 0 : ~UniformDist() GRPC_OVERRIDE {}
94 0 : double operator()(double uni) const GRPC_OVERRIDE {
95 0 : return uni * range_ + lo_;
96 : }
97 :
98 : private:
99 : double lo_;
100 : double range_;
101 : };
102 :
103 : // DetDist provides a random distribution with interarrival time
104 : // of val. Note that this is not additive, so using this on multiple
105 : // flows of control (threads within the same client or separate
106 : // clients) will not preserve any deterministic interarrival gap across
107 : // requests.
108 :
109 : class DetDist GRPC_FINAL : public RandomDist {
110 : public:
111 0 : explicit DetDist(double val) : val_(val) {}
112 0 : ~DetDist() GRPC_OVERRIDE {}
113 0 : double operator()(double uni) const GRPC_OVERRIDE { return val_; }
114 :
115 : private:
116 : double val_;
117 : };
118 :
119 : // ParetoDist provides a random distribution with interarrival time
120 : // spread according to a Pareto (heavy-tailed) distribution. In this
121 : // model, many interarrival times are close to the base, but a sufficient
122 : // number will be high (up to infinity) as to disturb the mean. It is a
123 : // good representation of the response times of data center jobs. See
124 : // http://en.wikipedia.org/wiki/Pareto_distribution
125 :
126 : class ParetoDist GRPC_FINAL : public RandomDist {
127 : public:
128 0 : ParetoDist(double base, double alpha)
129 0 : : base_(base), alpha_recip_(1.0 / alpha) {}
130 0 : ~ParetoDist() GRPC_OVERRIDE {}
131 0 : double operator()(double uni) const GRPC_OVERRIDE {
132 : // Note: Use 1.0-uni above to avoid div by zero if uni is 0
133 0 : return base_ / pow(1.0 - uni, alpha_recip_);
134 : }
135 :
136 : private:
137 : double base_;
138 : double alpha_recip_;
139 : };
140 :
141 : // A class library for generating pseudo-random interarrival times
142 : // in an efficient re-entrant way. The random table is built at construction
143 : // time, and each call must include the thread id of the invoker
144 :
145 : class InterarrivalTimer {
146 : public:
147 6 : InterarrivalTimer() {}
148 1 : void init(const RandomDist& r, int threads, int entries = 1000000) {
149 1000001 : for (int i = 0; i < entries; i++) {
150 : // rand is the only choice that is portable across POSIX and Windows
151 : // and that supports new and old compilers
152 1000000 : const double uniform_0_1 = rand() / RAND_MAX;
153 : random_table_.push_back(
154 1000000 : std::chrono::nanoseconds(static_cast<int64_t>(1e9 * r(uniform_0_1))));
155 : }
156 : // Now set up the thread positions
157 9 : for (int i = 0; i < threads; i++) {
158 8 : thread_posns_.push_back(random_table_.begin() + (entries * i) / threads);
159 : }
160 1 : }
161 6 : virtual ~InterarrivalTimer(){};
162 :
163 254675 : std::chrono::nanoseconds operator()(int thread_num) {
164 254675 : auto ret = *(thread_posns_[thread_num]++);
165 254683 : if (thread_posns_[thread_num] == random_table_.end())
166 0 : thread_posns_[thread_num] = random_table_.begin();
167 254671 : return ret;
168 : }
169 :
170 : private:
171 : typedef std::vector<std::chrono::nanoseconds> time_table;
172 : std::vector<time_table::const_iterator> thread_posns_;
173 : time_table random_table_;
174 : };
175 : }
176 : }
177 :
178 : #endif
|