1 | // Licensed to the Apache Software Foundation (ASF) under one |
2 | // or more contributor license agreements. See the NOTICE file |
3 | // distributed with this work for additional information |
4 | // regarding copyright ownership. The ASF licenses this file |
5 | // to you under the Apache License, Version 2.0 (the |
6 | // "License"); you may not use this file except in compliance |
7 | // with the License. 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, |
12 | // software distributed under the License is distributed on an |
13 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
14 | // KIND, either express or implied. See the License for the |
15 | // specific language governing permissions and limitations |
16 | // under the License. |
17 | |
18 | // Date: Wed Jul 29 23:25:43 CST 2015 |
19 | |
20 | #ifndef BVAR_WINDOW_H |
21 | #define BVAR_WINDOW_H |
22 | |
23 | #include <limits> // std::numeric_limits |
24 | #include <math.h> // round |
25 | #include <gflags/gflags_declare.h> |
26 | #include "butil/logging.h" // LOG |
27 | #include "bvar/detail/sampler.h" |
28 | #include "bvar/detail/series.h" |
29 | #include "bvar/variable.h" |
30 | |
31 | namespace bvar { |
32 | |
33 | DECLARE_int32(bvar_dump_interval); |
34 | |
35 | enum SeriesFrequency { |
36 | SERIES_IN_WINDOW = 0, |
37 | SERIES_IN_SECOND = 1 |
38 | }; |
39 | |
40 | namespace detail { |
41 | // Just for constructor reusing of Window<> |
42 | template <typename R, SeriesFrequency series_freq> |
43 | class WindowBase : public Variable { |
44 | public: |
45 | typedef typename R::value_type value_type; |
46 | typedef typename R::sampler_type sampler_type; |
47 | |
48 | class SeriesSampler : public detail::Sampler { |
49 | public: |
50 | struct Op { |
51 | explicit Op(R* var) : _var(var) {} |
52 | void operator()(value_type& v1, const value_type& v2) const { |
53 | _var->op()(v1, v2); |
54 | } |
55 | private: |
56 | R* _var; |
57 | }; |
58 | SeriesSampler(WindowBase* owner, R* var) |
59 | : _owner(owner), _series(Op(var)) {} |
60 | ~SeriesSampler() {} |
61 | void take_sample() override { |
62 | if (series_freq == SERIES_IN_SECOND) { |
63 | // Get one-second window value for PerSecond<>, otherwise the |
64 | // "smoother" plot may hide peaks. |
65 | _series.append(_owner->get_value(1)); |
66 | } else { |
67 | // Get the value inside the full window. "get_value(1)" is |
68 | // incorrect when users intend to see aggregated values of |
69 | // the full window in the plot. |
70 | _series.append(_owner->get_value()); |
71 | } |
72 | } |
73 | void describe(std::ostream& os) { _series.describe(os, NULL); } |
74 | private: |
75 | WindowBase* _owner; |
76 | detail::Series<value_type, Op> _series; |
77 | }; |
78 | |
79 | WindowBase(R* var, time_t window_size) |
80 | : _var(var) |
81 | , _window_size(window_size > 0 ? window_size : FLAGS_bvar_dump_interval) |
82 | , _sampler(var->get_sampler()) |
83 | , _series_sampler(NULL) { |
84 | CHECK_EQ(0, _sampler->set_window_size(_window_size)); |
85 | } |
86 | |
87 | ~WindowBase() { |
88 | hide(); |
89 | if (_series_sampler) { |
90 | _series_sampler->destroy(); |
91 | _series_sampler = NULL; |
92 | } |
93 | } |
94 | |
95 | bool get_span(time_t window_size, detail::Sample<value_type>* result) const { |
96 | return _sampler->get_value(window_size, result); |
97 | } |
98 | |
99 | bool get_span(detail::Sample<value_type>* result) const { |
100 | return get_span(_window_size, result); |
101 | } |
102 | |
103 | virtual value_type get_value(time_t window_size) const { |
104 | detail::Sample<value_type> tmp; |
105 | if (get_span(window_size, &tmp)) { |
106 | return tmp.data; |
107 | } |
108 | return value_type(); |
109 | } |
110 | |
111 | value_type get_value() const { return get_value(_window_size); } |
112 | |
113 | void describe(std::ostream& os, bool quote_string) const override { |
114 | if (butil::is_same<value_type, std::string>::value && quote_string) { |
115 | os << '"' << get_value() << '"'; |
116 | } else { |
117 | os << get_value(); |
118 | } |
119 | } |
120 | |
121 | #ifdef BAIDU_INTERNAL |
122 | void get_value(boost::any* value) const override { *value = get_value(); } |
123 | #endif |
124 | |
125 | time_t window_size() const { return _window_size; } |
126 | |
127 | int describe_series(std::ostream& os, const SeriesOptions& options) const override { |
128 | if (_series_sampler == NULL) { |
129 | return 1; |
130 | } |
131 | if (!options.test_only) { |
132 | _series_sampler->describe(os); |
133 | } |
134 | return 0; |
135 | } |
136 | |
137 | void get_samples(std::vector<value_type> *samples) const { |
138 | samples->clear(); |
139 | samples->reserve(_window_size); |
140 | return _sampler->get_samples(samples, _window_size); |
141 | } |
142 | |
143 | protected: |
144 | int expose_impl(const butil::StringPiece& prefix, |
145 | const butil::StringPiece& name, |
146 | DisplayFilter display_filter) override { |
147 | const int rc = Variable::expose_impl(prefix, name, display_filter); |
148 | if (rc == 0 && |
149 | _series_sampler == NULL && |
150 | FLAGS_save_series) { |
151 | _series_sampler = new SeriesSampler(this, _var); |
152 | _series_sampler->schedule(); |
153 | } |
154 | return rc; |
155 | } |
156 | |
157 | R* _var; |
158 | time_t _window_size; |
159 | sampler_type* _sampler; |
160 | SeriesSampler* _series_sampler; |
161 | }; |
162 | |
163 | } // namespace detail |
164 | |
165 | // Get data within a time window. |
166 | // The time unit is 1 second fixed. |
167 | // Window relies on other bvar which should be constructed before this window |
168 | // and destructs after this window. |
169 | |
170 | // R must: |
171 | // - have get_sampler() (not require thread-safe) |
172 | // - defined value_type and sampler_type |
173 | template <typename R, SeriesFrequency series_freq = SERIES_IN_WINDOW> |
174 | class Window : public detail::WindowBase<R, series_freq> { |
175 | typedef detail::WindowBase<R, series_freq> Base; |
176 | typedef typename R::value_type value_type; |
177 | typedef typename R::sampler_type sampler_type; |
178 | public: |
179 | // Different from PerSecond, we require window_size here because get_value |
180 | // of Window is largely affected by window_size while PerSecond is not. |
181 | Window(R* var, time_t window_size) : Base(var, window_size) {} |
182 | Window(const butil::StringPiece& name, R* var, time_t window_size) |
183 | : Base(var, window_size) { |
184 | this->expose(name); |
185 | } |
186 | Window(const butil::StringPiece& prefix, |
187 | const butil::StringPiece& name, R* var, time_t window_size) |
188 | : Base(var, window_size) { |
189 | this->expose_as(prefix, name); |
190 | } |
191 | }; |
192 | |
193 | // Get data per second within a time window. |
194 | // The only difference between PerSecond and Window is that PerSecond divides |
195 | // the data by time duration. |
196 | template <typename R> |
197 | class PerSecond : public detail::WindowBase<R, SERIES_IN_SECOND> { |
198 | typedef detail::WindowBase<R, SERIES_IN_SECOND> Base; |
199 | typedef typename R::value_type value_type; |
200 | typedef typename R::sampler_type sampler_type; |
201 | public: |
202 | // If window_size is non-positive or absent, use FLAGS_bvar_dump_interval. |
203 | PerSecond(R* var) : Base(var, -1) {} |
204 | PerSecond(R* var, time_t window_size) : Base(var, window_size) {} |
205 | PerSecond(const butil::StringPiece& name, R* var) : Base(var, -1) { |
206 | this->expose(name); |
207 | } |
208 | PerSecond(const butil::StringPiece& name, R* var, time_t window_size) |
209 | : Base(var, window_size) { |
210 | this->expose(name); |
211 | } |
212 | PerSecond(const butil::StringPiece& prefix, |
213 | const butil::StringPiece& name, R* var) |
214 | : Base(var, -1) { |
215 | this->expose_as(prefix, name); |
216 | } |
217 | PerSecond(const butil::StringPiece& prefix, |
218 | const butil::StringPiece& name, R* var, time_t window_size) |
219 | : Base(var, window_size) { |
220 | this->expose_as(prefix, name); |
221 | } |
222 | |
223 | value_type get_value(time_t window_size) const override { |
224 | detail::Sample<value_type> s; |
225 | this->get_span(window_size, &s); |
226 | // We may test if the multiplication overflows and use integral ops |
227 | // if possible. However signed/unsigned 32-bit/64-bit make the solution |
228 | // complex. Since this function is not called often, we use floating |
229 | // point for simplicity. |
230 | if (s.time_us <= 0) { |
231 | return static_cast<value_type>(0); |
232 | } |
233 | if (butil::is_floating_point<value_type>::value) { |
234 | return static_cast<value_type>(s.data * 1000000.0 / s.time_us); |
235 | } else { |
236 | return static_cast<value_type>(round(s.data * 1000000.0 / s.time_us)); |
237 | } |
238 | } |
239 | }; |
240 | |
241 | } // namespace bvar |
242 | |
243 | #endif //BVAR_WINDOW_H |
244 | |