perf/x86: Improve accuracy of perf/sched clock
When TSC is stable perf/sched clock is based on it. However the conversion from cycles to nanoseconds is not as accurate as it could be. Because CYC2NS_SCALE_FACTOR is 10, the accuracy is +/- 1/2048 The change is to calculate the maximum shift that results in a multiplier that is still a 32-bit number. For example all frequencies over 1 GHz will have a shift of 32, making the accuracy of the conversion +/- 1/(2^33). That is achieved by using the 'clocks_calc_mult_shift()' function. Signed-off-by: Adrian Hunter <adrian.hunter@intel.com> Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org> Cc: Andy Lutomirski <luto@amacapital.net> Cc: Arnaldo Carvalho de Melo <acme@kernel.org> Cc: Arnaldo Carvalho de Melo <acme@redhat.com> Cc: Jiri Olsa <jolsa@redhat.com> Cc: Linus Torvalds <torvalds@linux-foundation.org> Cc: Peter Zijlstra <peterz@infradead.org> Cc: Stephane Eranian <eranian@google.com> Cc: Thomas Gleixner <tglx@linutronix.de> Cc: Vince Weaver <vincent.weaver@maine.edu> Link: http://lkml.kernel.org/r/1440147918-22250-1-git-send-email-adrian.hunter@intel.com Signed-off-by: Ingo Molnar <mingo@kernel.org>
This commit is contained in:
parent
216dcaf290
commit
b20112edea
@ -167,21 +167,20 @@ static void cyc2ns_write_end(int cpu, struct cyc2ns_data *data)
|
|||||||
* ns = cycles * cyc2ns_scale / SC
|
* ns = cycles * cyc2ns_scale / SC
|
||||||
*
|
*
|
||||||
* And since SC is a constant power of two, we can convert the div
|
* And since SC is a constant power of two, we can convert the div
|
||||||
* into a shift.
|
* into a shift. The larger SC is, the more accurate the conversion, but
|
||||||
|
* cyc2ns_scale needs to be a 32-bit value so that 32-bit multiplication
|
||||||
|
* (64-bit result) can be used.
|
||||||
*
|
*
|
||||||
* We can use khz divisor instead of mhz to keep a better precision, since
|
* We can use khz divisor instead of mhz to keep a better precision.
|
||||||
* cyc2ns_scale is limited to 10^6 * 2^10, which fits in 32 bits.
|
|
||||||
* (mathieu.desnoyers@polymtl.ca)
|
* (mathieu.desnoyers@polymtl.ca)
|
||||||
*
|
*
|
||||||
* -johnstul@us.ibm.com "math is hard, lets go shopping!"
|
* -johnstul@us.ibm.com "math is hard, lets go shopping!"
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#define CYC2NS_SCALE_FACTOR 10 /* 2^10, carefully chosen */
|
|
||||||
|
|
||||||
static void cyc2ns_data_init(struct cyc2ns_data *data)
|
static void cyc2ns_data_init(struct cyc2ns_data *data)
|
||||||
{
|
{
|
||||||
data->cyc2ns_mul = 0;
|
data->cyc2ns_mul = 0;
|
||||||
data->cyc2ns_shift = CYC2NS_SCALE_FACTOR;
|
data->cyc2ns_shift = 0;
|
||||||
data->cyc2ns_offset = 0;
|
data->cyc2ns_offset = 0;
|
||||||
data->__count = 0;
|
data->__count = 0;
|
||||||
}
|
}
|
||||||
@ -215,14 +214,14 @@ static inline unsigned long long cycles_2_ns(unsigned long long cyc)
|
|||||||
|
|
||||||
if (likely(data == tail)) {
|
if (likely(data == tail)) {
|
||||||
ns = data->cyc2ns_offset;
|
ns = data->cyc2ns_offset;
|
||||||
ns += mul_u64_u32_shr(cyc, data->cyc2ns_mul, CYC2NS_SCALE_FACTOR);
|
ns += mul_u64_u32_shr(cyc, data->cyc2ns_mul, data->cyc2ns_shift);
|
||||||
} else {
|
} else {
|
||||||
data->__count++;
|
data->__count++;
|
||||||
|
|
||||||
barrier();
|
barrier();
|
||||||
|
|
||||||
ns = data->cyc2ns_offset;
|
ns = data->cyc2ns_offset;
|
||||||
ns += mul_u64_u32_shr(cyc, data->cyc2ns_mul, CYC2NS_SCALE_FACTOR);
|
ns += mul_u64_u32_shr(cyc, data->cyc2ns_mul, data->cyc2ns_shift);
|
||||||
|
|
||||||
barrier();
|
barrier();
|
||||||
|
|
||||||
@ -256,12 +255,11 @@ static void set_cyc2ns_scale(unsigned long cpu_khz, int cpu)
|
|||||||
* time function is continuous; see the comment near struct
|
* time function is continuous; see the comment near struct
|
||||||
* cyc2ns_data.
|
* cyc2ns_data.
|
||||||
*/
|
*/
|
||||||
data->cyc2ns_mul =
|
clocks_calc_mult_shift(&data->cyc2ns_mul, &data->cyc2ns_shift, cpu_khz,
|
||||||
DIV_ROUND_CLOSEST(NSEC_PER_MSEC << CYC2NS_SCALE_FACTOR,
|
NSEC_PER_MSEC, 0);
|
||||||
cpu_khz);
|
|
||||||
data->cyc2ns_shift = CYC2NS_SCALE_FACTOR;
|
|
||||||
data->cyc2ns_offset = ns_now -
|
data->cyc2ns_offset = ns_now -
|
||||||
mul_u64_u32_shr(tsc_now, data->cyc2ns_mul, CYC2NS_SCALE_FACTOR);
|
mul_u64_u32_shr(tsc_now, data->cyc2ns_mul, data->cyc2ns_shift);
|
||||||
|
|
||||||
cyc2ns_write_end(cpu, data);
|
cyc2ns_write_end(cpu, data);
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user