]> git.proxmox.com Git - ceph.git/blob - ceph/src/common/Cycles.cc
add subtree-ish sources for 12.0.3
[ceph.git] / ceph / src / common / Cycles.cc
1 // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2 // vim: ts=8 sw=2 smarttab
3 /*
4 * Ceph - scalable distributed file system
5 *
6 * Copyright (C) 2014 UnitedStack <haomai@unitedstack.com>
7 *
8 * Author: Haomai Wang <haomaiwang@gmail.com>
9 *
10 * This is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU Lesser General Public
12 * License version 2.1, as published by the Free Software
13 * Foundation. See file COPYING.
14 *
15 */
16 /* Copyright (c) 2011-2014 Stanford University
17 *
18 * Permission to use, copy, modify, and distribute this software for any
19 * purpose with or without fee is hereby granted, provided that the above
20 * copyright notice and this permission notice appear in all copies.
21 *
22 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR(S) DISCLAIM ALL WARRANTIES
23 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
24 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL AUTHORS BE LIABLE FOR
25 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
26 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
27 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
28 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
29 */
30
31
32 #include <errno.h>
33 #include <sys/time.h>
34
35 #include "errno.h"
36 #include "debug.h"
37 #include "Initialize.h"
38 #include "Cycles.h"
39
40 double Cycles::cycles_per_sec = 0;
41
42 /**
43 * Perform once-only overall initialization for the Cycles class, such
44 * as calibrating the clock frequency. This method must be called
45 * before using the Cycles module.
46 *
47 * It is not initialized by default because the timing loops cause
48 * general process startup times to balloon
49 * (http://tracker.ceph.com/issues/15225).
50 */
51 void Cycles::init()
52 {
53 if (cycles_per_sec != 0)
54 return;
55
56 // Skip initialization if rtdsc is not implemented
57 if (rdtsc() == 0)
58 return;
59
60 // Compute the frequency of the fine-grained CPU timer: to do this,
61 // take parallel time readings using both rdtsc and gettimeofday.
62 // After 10ms have elapsed, take the ratio between these readings.
63
64 struct timeval start_time, stop_time;
65 uint64_t micros;
66 double old_cycles;
67
68 // There is one tricky aspect, which is that we could get interrupted
69 // between calling gettimeofday and reading the cycle counter, in which
70 // case we won't have corresponding readings. To handle this (unlikely)
71 // case, compute the overall result repeatedly, and wait until we get
72 // two successive calculations that are within 0.1% of each other.
73 old_cycles = 0;
74 while (1) {
75 if (gettimeofday(&start_time, NULL) != 0) {
76 assert(0 == "couldn't read clock");
77 }
78 uint64_t start_cycles = rdtsc();
79 while (1) {
80 if (gettimeofday(&stop_time, NULL) != 0) {
81 assert(0 == "couldn't read clock");
82 }
83 uint64_t stop_cycles = rdtsc();
84 micros = (stop_time.tv_usec - start_time.tv_usec) +
85 (stop_time.tv_sec - start_time.tv_sec)*1000000;
86 if (micros > 10000) {
87 cycles_per_sec = static_cast<double>(stop_cycles - start_cycles);
88 cycles_per_sec = 1000000.0*cycles_per_sec/ static_cast<double>(micros);
89 break;
90 }
91 }
92 double delta = cycles_per_sec/1000.0;
93 if ((old_cycles > (cycles_per_sec - delta)) &&
94 (old_cycles < (cycles_per_sec + delta))) {
95 return;
96 }
97 old_cycles = cycles_per_sec;
98 }
99 }
100
101 /**
102 * Return the number of CPU cycles per second.
103 */
104 double Cycles::per_second()
105 {
106 return get_cycles_per_sec();
107 }
108
109 /**
110 * Given an elapsed time measured in cycles, return a floating-point number
111 * giving the corresponding time in seconds.
112 * \param cycles
113 * Difference between the results of two calls to rdtsc.
114 * \param cycles_per_sec
115 * Optional parameter to specify the frequency of the counter that #cycles
116 * was taken from. Useful when converting a remote machine's tick counter
117 * to seconds. The default value of 0 will use the local processor's
118 * computed counter frequency.
119 * \return
120 * The time in seconds corresponding to cycles.
121 */
122 double Cycles::to_seconds(uint64_t cycles, double cycles_per_sec)
123 {
124 if (cycles_per_sec == 0)
125 cycles_per_sec = get_cycles_per_sec();
126 return static_cast<double>(cycles)/cycles_per_sec;
127 }
128
129 /**
130 * Given a time in seconds, return the number of cycles that it
131 * corresponds to.
132 * \param seconds
133 * Time in seconds.
134 * \param cycles_per_sec
135 * Optional parameter to specify the frequency of the counter that #cycles
136 * was taken from. Useful when converting a remote machine's tick counter
137 * to seconds. The default value of 0 will use the local processor's
138 * computed counter frequency.
139 * \return
140 * The approximate number of cycles corresponding to #seconds.
141 */
142 uint64_t Cycles::from_seconds(double seconds, double cycles_per_sec)
143 {
144 if (cycles_per_sec == 0)
145 cycles_per_sec = get_cycles_per_sec();
146 return (uint64_t) (seconds*cycles_per_sec + 0.5);
147 }
148
149 /**
150 * Given an elapsed time measured in cycles, return an integer
151 * giving the corresponding time in microseconds. Note: to_seconds()
152 * is faster than this method.
153 * \param cycles
154 * Difference between the results of two calls to rdtsc.
155 * \param cycles_per_sec
156 * Optional parameter to specify the frequency of the counter that #cycles
157 * was taken from. Useful when converting a remote machine's tick counter
158 * to seconds. The default value of 0 will use the local processor's
159 * computed counter frequency.
160 * \return
161 * The time in microseconds corresponding to cycles (rounded).
162 */
163 uint64_t Cycles::to_microseconds(uint64_t cycles, double cycles_per_sec)
164 {
165 return to_nanoseconds(cycles, cycles_per_sec) / 1000;
166 }
167
168 /**
169 * Given an elapsed time measured in cycles, return an integer
170 * giving the corresponding time in nanoseconds. Note: to_seconds()
171 * is faster than this method.
172 * \param cycles
173 * Difference between the results of two calls to rdtsc.
174 * \param cycles_per_sec
175 * Optional parameter to specify the frequency of the counter that #cycles
176 * was taken from. Useful when converting a remote machine's tick counter
177 * to seconds. The default value of 0 will use the local processor's
178 * computed counter frequency.
179 * \return
180 * The time in nanoseconds corresponding to cycles (rounded).
181 */
182 uint64_t Cycles::to_nanoseconds(uint64_t cycles, double cycles_per_sec)
183 {
184 if (cycles_per_sec == 0)
185 cycles_per_sec = get_cycles_per_sec();
186 return (uint64_t) (1e09*static_cast<double>(cycles)/cycles_per_sec + 0.5);
187 }
188
189 /**
190 * Given a number of nanoseconds, return an approximate number of
191 * cycles for an equivalent time length.
192 * \param ns
193 * Number of nanoseconds.
194 * \param cycles_per_sec
195 * Optional parameter to specify the frequency of the counter that #cycles
196 * was taken from. Useful when converting a remote machine's tick counter
197 * to seconds. The default value of 0 will use the local processor's
198 * computed counter frequency.
199 * \return
200 * The approximate number of cycles for the same time length.
201 */
202 uint64_t
203 Cycles::from_nanoseconds(uint64_t ns, double cycles_per_sec)
204 {
205 if (cycles_per_sec == 0)
206 cycles_per_sec = get_cycles_per_sec();
207 return (uint64_t) (static_cast<double>(ns)*cycles_per_sec/1e09 + 0.5);
208 }
209
210 /**
211 * Busy wait for a given number of microseconds.
212 * Callers should use this method in most reasonable cases as opposed to
213 * usleep for accurate measurements. Calling usleep may put the the processor
214 * in a low power mode/sleep state which reduces the clock frequency.
215 * So, each time the process/thread wakes up from usleep, it takes some time
216 * to ramp up to maximum frequency. Thus meausrements often incur higher
217 * latencies.
218 * \param us
219 * Number of microseconds.
220 */
221 void
222 Cycles::sleep(uint64_t us)
223 {
224 uint64_t stop = Cycles::rdtsc() + Cycles::from_nanoseconds(1000*us);
225 while (Cycles::rdtsc() < stop);
226 }