One correction, that also makes me worry a little.

On 03.09.15 01:29, Ivan Khoronzhuk wrote:
Hi, Petri

We have to look at it proceeding from performance, platform portability and 
simplicity

If you want to split on hi-res time and low-res they must have separate 
functions and
be not under one common opaque time in order to not break hi-res measurements.

But in fact you split the same quality timers, farther below...

On 02.09.15 18:21, Savolainen, Petri (Nokia - FI/Espoo) wrote:
Hi,


I think we need to restart the time API discussion and specify it only wall 
time in mind.

Let's suppose.

CPU cycle count APIs can be tuned as a next step. CPU cycle counters are 
affected by frequency scaling, which makes those difficult to use for counting 
linear, real time.
 The time API should specify an easy way to check and use the real, wall clock 
time.
We need at least one time source that will not wrap in years - here it's the 
"global" time
(e.g. in POSIX it's CLOCK_MONOTONIC).

Don't mix it with this API.
CLOCK_MONOTONIC is guaranteed by OS, that can handle wraps, OS can use 
interrupts for that, you cannot.

Also it must be zero at platform init and begin count time when application 
starts. For each application that starts.
Can you guarantee that it's inited to zero for each platform? I hesitate to 
answer on this question.

But let's suppose that we can guarantee it.
In this case time should be aligned for all executed applications to start from 
zero.
Let's suppose some start_time = odp_time() at application init.
As it was noted earlier, in some fast loop, init_count must be extracted in 
diff function.
I'm not talking event about checking of time type, as you are going to put all 
of them under one type.

  Global time can be also compared between threads. Local time is defined for 
optimizing short interval time checks.

In fact global has same quality as local. As noted earlier, global can be 
emulated with local.

It's not always true. As we anyway will have some out of sync. It can require 
to periodically synchronize the counters.
We also cannot guarantee it.
That's why every multi-core board has to have common timer/counter on SoC.
In my case it runs with the same rate as arch arm timer, that is local.
I want to believe that for other platforms also, but I can't be sure.
In another case it's hard to guarantee global time availability also.

So we can have situation that local time API is only variant here.
For wall time, common timer or RTC should be used, I no see another variant
And it should be separate API, with not hard requirement.

Why we need to split them in this case? It'll add load on user only.

It's thread local, may wrap sooner than global time, but may be lower overhead 
to use.

They are both 64-bit. You are going to use the same function for both, overhead 
the same.


There could be actually four time bases defined in the API, if we'd want to 
optimize for each use case (global.hi_res, global.low_res, local.hi_res and 
local.low_res).

I'd propose to have only two and give system specific way to configure those 
(rate and duration).

What do you mean "giv system way...", do you mean add some API?
If so, I disagree. It's not ODP responsibility and it's not every platform 
applicable.

 Typical config would be global.low_res and local.hi_res. User can check hz and 
max time value (wrap around time) with odp_time_info() and adapt (fall back to 
use global time) if e.g. local time wraps too quickly (e.g. in 4 sec).

If this time wrap every 4s, it shouldn't be used at all...(any 32-bits)


See the proposal under.

-Petri




//
// Use cases
//
//             high resolution                 low resolution
//             short interval                  long interval
//             low overhead                    high overhead
//
//
//  global     timestamp packets or       |    timestamp log entries or
//             other global resources     |    other global resources
//             at high rate               |    at low rate
//                                        |
//             ---------------------------+------------------------------
//                                        |
//  local      timestamp and sort items   |    measure execution time over
//             in thread local work queue,|    many iterations or over
//             measure execution time     |    a "long" function
//             of a "short" function,     |
//             spin and wait for a short  |
//             while
//
//

No see reason to overload user with this stuff.
In fact we always need one hi-resolution time with best quality, no matter what 
we measure.
No matter how resolution it has, it should be the max that platform can provide 
for that.
At this moment all counters are 64-bit and can not wrap for years.
On my opinion,32-bit counter we shouldn't take into account.


// time in nsec
// renamed to leave room for sec or other units in the future
#define ODP_TIME_NS_USEC 1000ULL       /**< Microsecond in nsec */
#define ODP_TIME_NS_MSEC 1000000ULL    /**< Millisecond in nsec */
#define ODP_TIME_NS_SEC  1000000000ULL /**< Second in nsec */
#define ODP_TIME_NS_DAY  ((24*60*60)*ODP_TIME_NS_SEC) /**< Day in nsec */



// Abstract time type
// Implementation specific type, includes e.g.
// - counter value
// - potentially other information: global vs. local time, ...
typedef odp_time_t

This type can be added only with one aim - ask user to use appropriate API that
can handle wraps correctly. In another case, like with global time you are
proposing (no wrap), uint64_t can be used, no need to overload API with 
odp_time_t
and APIs like diff, cmp, etc.


// Get global time
// Global time is common over all threads. Global timestamps can be compared
// between threads.
odp_time_t odp_time(void);

// Get thread local time
// Thread local time is only meaningful for the calling thread. It cannot be
// compare with other timestamps (global or local from other threads).
// May run from different clock source and different rate than global time.
// User must take care not to mix local and global time values in API calls.
odp_time_t odp_time_local(void);

I dislike the idea of local time. Theoretically it can be added, but I no see 
reason for that.
Even if it's required, it should be handled with separate functions, as 
according to RFC it can
overlap, global cannot. In every function the time type has to be checked and 
different approach chosen.
It's time consuming redundancy for short periods and this reduces the actual 
resolution.


// Compare time values
//
// Check if t1 is before t2 in absolute time, or if interval t1 is shorter
// than interval t2
//
// -1: t2 <  t1
//  0: t2 == t1
//  1: t2 >  t1
int odp_time_cmp(odp_time_t t1, odp_time_t t2);

This function, according to RFC, must behave differently for local and global 
time.
And use cases also different, for time than can wrap, it can be used only for 
ranges.
But, again, I dislike to guarantee any timer linearity.
I added this function with only one intention - simply compare time ranges, not 
more.


// Sum of t1 and t2
//
// User can sum timestamps or accumulate multiple intervals before
// comparing or converting to nsec
odp_time_t odp_time_sum(odp_time_t t1, odp_time_t t2);

// Time difference between t1 and t2
//
// Calculate interval from timestamp t1 to t2, or difference of two intervals.
// T2 must be the latter timestamp, or the longer interval (t2 >= t1).
// Use cmp() first, if don't know which timestamp is the latter/longer.

Event if suppose that it's split on local/global
you cannot use it to compare timestamps that can wrap (local)
Compare can be used only to compare RANGES.

odp_time_t odp_time_diff(odp_time_t t1, odp_time_t t2);

// Convert ODP time to wall clock time in nsec
//
// Wall clock time advances linearly in realtime and starts from 0 in ODP init.
//
// Global time must not wrap in several years (max time value is defined by
// info.global_nsec_max). Local time may have shorter wrap around time
// (info.local_nsec_max) than global, but it's also recommended to be years.
//
// Global and local time may run from different time base and thus result
// different nsec values.
uint64_t odp_time_to_ns(odp_time_t time);

As I see it can be used for "local" time also.
You cannot get wall clock time from time counter that can wrap with this 
function.
It can be done only in this way:

start_time = odp_time(); // at init.
....

odp_time_ns(odp_time_diff(start_time, odp_time()))


// convert nsec value to global time
odp_time_t odp_time_from_ns(uint64_t ns);

// convert nsec value to local time
odp_time_t odp_time_local_from_ns(uint64_t ns);

// Time info structure
typedef struct {
    // Global timestamp resolution in hz
    uint64_t global_hz;
    // Max global time value in nsec. Global time values (timestamps or
    // intervals) larger than this are not handled correctly.
       // Global wall clock time wraps back to zero after this value.
    uint64_t global_nsec_max;

User don't need to worry about this parameter.
Why do we need this? I no see any usecase. Only if user wants catch wraps.
But why then add wall global time if he needs to worry about this. Strange.

    // Local timestamp resolution in hz
    uint64_t local_hz;
    // Max local time value in nsec. Local time values (timestamps or
    // intervals) larger than this are not handled correctly.
       // Local wall clock time wraps back to zero after this value.
    uint64_t local_nsec_max;
} odp_time_info_t;

// Time info request
//
// Fill in time info struct. User can check resolutions and max time values¨
// in nsec.
//
// 0 on success
// <0 on failure
int odp_time_info(odp_time_info_t *info);
_______________________________________________
lng-odp mailing list
[email protected]
https://lists.linaro.org/mailman/listinfo/lng-odp


In summary:
* I like wall time, but:
   - it requires to extract init value in places where it's not needed.
   - requires to guarantee that timer is set counter to 0 at init.
   - can be replaced with:
       odp_time_diff(start_init_time, odp_time()) // give you wall time,
                                                  // then convert to ns if you 
need.
     which doesn't require guarantee to be 0 at init.

* According local time
  - it increases complexity.
  - it requires to hold different types of time under opaque type, thus
  - it requires each time check the type of time under odp_time_diff(), which
       can be used in places sensible for that.
  - if local counters have better characteristics they can emulate global timer,
    in turn global timer can be used everywhere. And doesn't matter if they are
    the same on some platforms, you should worry about it in application anyway.

I propose to use always global time and use API, that is enough for all cases:
Mostly it includes and follows existent time API:

odp_time_t odp_time(void);
odp_time_t odp_time_diff(odp_time_t t1, odp_time_t t2); // ranges and timestamps
odp_time_t odp_time_sum(odp_time_t t1, odp_time_t t2);
uint64_t odp_time_to_ns(odp_time_t time);
odp_time_t odp_time_from_ns(uint64_t ns);
int odp_time_cmp(odp_time_t t1, odp_time_t t2); // only ranges
uint64_t odp_time_to_u64(odp_time_t time); // debugg purposes
ODP_TIME_NULL // for init and comparison


--
Regards,
Ivan Khoronzhuk
_______________________________________________
lng-odp mailing list
[email protected]
https://lists.linaro.org/mailman/listinfo/lng-odp

Reply via email to