Re: CPU Frequency scaling with LTTng

Mathieu Desnoyers <[email protected]> Mon, 12 Nov 2007 13:14:30 -0500
Newsgroups gmane.linux.kernel.tracing
Message-ID <20071112181430.GA757@Krystal>
* Richard Purdie ([email protected]) wrote:
> Hi Mathieu,
> 
> On Sun, 2007-11-11 at 19:58 -0500, Mathieu Desnoyers wrote:
> > * Richard Purdie ([email protected]) wrote:
> > > I've been experimenting with CPU frequency scaling and its effect on
> > > LTTng. I notice some of the header fields hint at planned support for
> > > frequency scaling but the userland tools (LTTV) don't seem to support
> > > it?
> > > 
> > 
> > Yes, this is something we have to address. LTTng should live correctly
> > with frequency scaling in UP systems, but won't give the correct elapsed
> > time in ns.
> 
> Yes, incorrect elapsed time and bunching of events which makes reading
> the LTT data much tricker.
> 
> > In SMP, the current version will detect non synchronized TSCs (which
> > happens when frequency scaling is used on x86, x86_64) and will fall
> > back on an hybrid clock based on the highest TSC count seen in the
> > system and a logical clock.
> 
> Right, the SMP case is tricky...
> 
> > > >From a kernel point of view the easiest way to handle it seemed to be to
> > > force a subbuffer switch immediately after the frequency change. This is
> > > relatively straightforward to do with minor alterations to existing
> > > code.
> > > 
> > 
> > Hrm, but you can end up having maaaany buffers. How frequent can the
> > frequency change be in the worse case ? 
> 
> I suspect the answer is very frequent in theory. In practise the
> governor smooths them out so I'd expect a maximum frequency of say 1-4Hz
> though.
> 
> I am using a different transport where the overhead involved in a buffer
> switch is less significant but I take your point.
> 
> > The idea is interesting and points out a basic need : we have to be able 
> > to extract frequency change events efficiently.
> [...]
> > > 
> > > Are there any plans to support frequency scaling and is this approach
> > > along the right lines? Any thoughts on the subject are welcome!
> > > 
> > 
> > I had a design idea for this, just did not have time to implement it. It
> > implies modification of both LTTng and LTTV :
> > 
> > - We should create a new channel "frequency" channel, which would always
> > be in "normal" mode (just like the facilities channel). Therefore, we
> > can read the frequency channel before reading the rest of the trace and
> > extract the information about each frequency change that happened during
> > tracing. We can then create a data structure that will map :
> > 
> > cycles and cpu number -> time
> > 
> > Events in this tracefile will have, just like any event, a timestamp
> > and, for event data, the new frequency.
> > 
> > In LTTng, we will need to record the frequency in an event that uses the
> > "force" flag (currently used for 64 bits heartbeat written just before
> > we activate tracing). It separates the timestamp generation from the
> > physical buffer boundaries. It is useful when we plan to do multiple
> > trace "start/pause/unpause/.../stop".
> > 
> > In LTTV, we would keep one binary tree per CPU in a trace, indexed by
> > cycles. We would create this tree by reading the frequency channel on a
> > per cpu tracefile basis, keeping a counter of the time increasing (in
> > ns), at the rate of the last frequency encountered.
> > 
> > Afterward, a query to this data structure would look like :
> > 
> > LttTime get_timestamp_from_cpu_cycles(guint64 cycles, guint cpu);
> > 
> > This O(log(n)) search would be useful when we look for the right block
> > containing the information looked for (it's already a O(log(n)) search).
> > However, for the sequential trace read, we would hook on the frequency
> > change events in ltt/tracefile.c (it would be an hardcoded event) and
> > keep track of the current frequency of each CPU. We could then compute
> > the current time in ns with minimal CPU time required.
> > 
> > What do you think of this ?
> 
> In trying the approach I mentioned I was keeping in mind the past
> discussions we've had where you wanted to keep timestamps simple and not
> need to rely on logged data to be able to interpret the timestamps
> themselves. Whilst that has been tricky on systems with limited TSCs, I
> think we've achieved a lot there and it works well and efficiently. 
> 
> Ideally, it would be good to be able to take a subbuffer on its own and
> be able interpret the timestamp data contained within without external
> references. If the start and end frequencies are in the header as per
> the existing format its possible to do that.
> 

We still have problems synchronizing different buffers together (across
CPUs on SMP, different channels on UP).

> Perhaps the way forward would be to allow the "physical" subbuffer to be
> further split into virtual sections. At each frequency change, we write
> a length to the active header effectively closing it and start a new
> header containing the frequency for the new data? Each physical buffer
> could then contain multiple virtual sections each at a given frequency.
> 
> This would allow a virtual "buffer switch" at each frequency change
> without the overhead of a physical buffer change.
> 
> Would that work?
> 

The buffers are used to perform fast O(log(n)) time seek in LTTV. It
relies on the fact that the timestamps are known at the physical buffer
boundary to do this. I am not sure this concept of "virtual" buffer
within the physical buffer will give that to us.


> I do like the simplicity of the current LTT timestamp approach and am
> just nervous about anything that complicates it too much!
> 

I agree : we must keep this as "dumb-stupidly-reviewable" as possible :)
What's the point in precise tracing information if we can't trust the
timestamps.

Which brings me to a problem that we will have to face to support
frequency scaling efficiently on SMP : since there is a delay between
the CPU frequency change and the moment the kernel is informed
(very likely interrupt latency + cycles spent in ISR before the event),
we have to deal with the fact that we will have a false knowledge of the
CPU frequency for the period that precedes the event. As time goes by,
this delta between CPUs will get worse.

Also, we have to think of a way to synchronize the time bases before
tracing starts. Currently, we are synchronized on the CPU's "0" cycle
(last time their TSC has been reset). However, since we don't keep track
of the frequency change history _before_ tracing starts (or should we
?), we would have to know as precisely as possible what tsc values are
equivalent right before we start tracing. Sadly, this operation is
costly and much more precise when done on an idle system; our results
could be much worse that the synchronization the kernel does at boot
time when the CPUs are mostly idle. See ltt/ltt-test-tsc.c to see my
attempt at doing a less intrusive test of TSC synchronicity. (I tried
not to disable interrupts for too long)

We could have to think about redoing this periodically to resynchronize
our trace timestamps, thus getting an upper bound on the timestamp
error. However, we would have to make sure this is not too intrusive
wrt interrupt latency.

I agree that we would have to write the timestamps and current frequency
at buffer boundaries, so we can easily seek if we have missing buffers.
However, we would rely on having a separate channel to keep track of
frequency changes across the lost buffers to be able to keep them
synchronized. This is why I suggest to keep this information in a
separate low traffic channel.

Mathieu


> Regards,
> 
> Richard
> 
> 

-- 
Mathieu Desnoyers
Computer Engineering Ph.D. Student, Ecole Polytechnique de Montreal
OpenPGP key fingerprint: 8CD5 52C3 8E3C 4140 715F  BA06 3F25 A8FE 3BAE 9A68