Project

General

Profile

Actions

Feature #8640

open

Add Time#elapsed to return nanoseconds since creation

Added by tenderlovemaking (Aaron Patterson) over 10 years ago. Updated about 2 years ago.

Status:
Open
Assignee:
-
Target version:
-
[ruby-core:56027]

Description

Hi,

We time many things in Rails (and so does lots of other code), and I've found that we spend lots of time allocating and subtracting time objects. For example:

start = Time.now

do stuff we want to time

finish = Time.now - start

It would be nice if we could just create one time object and grab the nanoseconds elapsed since the time object was created like so:

start = Time.now

do stuff we want to time

finished = start.elapsed # => nanoseconds elapsed.

I've attached a patch that implements this feature.


Files

elapsed.patch (1.95 KB) elapsed.patch tenderlovemaking (Aaron Patterson), 07/16/2013 03:39 AM

Related issues 3 (1 open2 closed)

Related to Ruby master - Feature #8658: Process.clock_gettimeClosed07/19/2013Actions
Related to Ruby master - Feature #8096: introduce Time.current_timestampFeedbackmatz (Yukihiro Matsumoto)Actions
Related to Ruby master - Feature #12173: `Time#till_now`OpenActions

Updated by duerst (Martin Dürst) over 10 years ago

I think this is a good idea. But since
duration = Time.now - start
is in seconds, I'd either keep seconds as units, or make the use of nanoseconds explicit, e.g. like this:
duration = start.elapsed_nanoseconds

Updated by phluid61 (Matthew Kerwin) over 10 years ago

+1

duerst (Martin Dürst) wrote:

I think this is a good idea. But since
duration = Time.now - start
is in seconds, I'd either keep seconds as units, or make the use of nanoseconds explicit, e.g. like this:
duration = start.elapsed_nanoseconds

Since this is a new feature I think we don't need to worry as much about existing functionality. Personally I prefer the function to return a higher-precision integer over a float.

I wouldn't complain if there was a kwarg, although I'm not sure which way it should switch: time_obj.elapsed(float: true) or time_obj.elapsed(nanoseconds: true)

Updated by duerst (Martin Dürst) over 10 years ago

phluid61 (Matthew Kerwin) wrote:

duerst (Martin Dürst) wrote:

duration = start.elapsed_nanoseconds

Since this is a new feature I think we don't need to worry as much about existing functionality.

It's going to be very confusing to have different methods on Time use different units. In some years, nobody will remember which method is new and which is old.

Updated by nobu (Nobuyoshi Nakada) over 10 years ago

phluid61 (Matthew Kerwin) wrote:

I wouldn't complain if there was a kwarg, although I'm not sure which way it should switch: time_obj.elapsed(float: true) or time_obj.elapsed(nanoseconds: true)

time_obj.elapsed(in: :nanoseconds) ?

Updated by phluid61 (Matthew Kerwin) over 10 years ago

duerst (Martin Dürst) wrote:

It's going to be very confusing to have different methods on Time use different units. In some years, nobody will remember which method is new and which is old.

You're right. It makes sense to exactly mimic time - other_time, and return the number of seconds in a float.

nobu (Nobuyoshi Nakada) wrote:

phluid61 (Matthew Kerwin) wrote:

I wouldn't complain if there was a kwarg, although I'm not sure which way it should switch: time_obj.elapsed(float: true) or time_obj.elapsed(nanoseconds: true)

time_obj.elapsed(in: :nanoseconds) ?

I had considered that, but I wonder if people aren't going to ask for (or expect) support for :milliseconds, :hours, :days, :aztec_great_cycles, etc. The rules about which symbols are valid seems a little arbitrary. And then I suppose the question could become: why are nanoseconds an integer, but seconds a float? And why doesn't Time#- accept the same kwarg? And so on with the bike-shedding. I don't know a good answer for most of these hypothetical questions, so I guess the short-term solution would be to make #elapsed be a short-cut for Time.now - self, and return a float number of seconds.

Also, I feel obliged to mention that this feature is related to #8096, FWIW.

Updated by Anonymous over 10 years ago

I'll echo what everyone else has said above by saying that I'm strongly against #elapsed returning nanoseconds. I'm happy with #elapsed_nanoseconds returning nanoseconds as an Integer, and #elapsed returning seconds as a Float though.

Updated by Anonymous over 10 years ago

On Wed, Jul 17, 2013 at 04:04:42AM +0900, charliesome (Charlie Somerville) wrote:

Issue #8640 has been updated by charliesome (Charlie Somerville).

I'll echo what everyone else has said above by saying that I'm strongly against #elapsed returning nanoseconds. I'm happy with #elapsed_nanoseconds returning nanoseconds as an Integer, and #elapsed returning seconds as a Float though.

elapsed_nanoseconds is fine with me. I will change the ticket title
(if I can).

--
Aaron Patterson
http://tenderlovemaking.com/

Updated by duerst (Martin Dürst) over 10 years ago

Hello Aaron,

On 2013/07/17 1:46, Aaron Patterson wrote:

Hi Martin,

On Tue, Jul 16, 2013 at 03:24:36PM +0900, duerst (Martin Dürst) wrote:

Issue #8640 has been updated by duerst (Martin Dürst).

I think this is a good idea. But since
duration = Time.now - start
is in seconds, I'd either keep seconds as units, or make the use of nanoseconds explicit, e.g. like this:
duration = start.elapsed_nanoseconds

I chose nanoseconds because it can be represented as an integer,

On a 32-bit machine, that works up to about 0.5 seconds. (On a 64-bit
machine, it's about 73 years.) After that, it will be a Bignum.

Regards, Martin.

so we
don't need to allocate Ruby objects (plus all the other benefits
integers buy us). So I'd really like to stick with nanoseconds as the
unit.

I'll propose both elapsed and elapsed_nanoseconds to matz. :-)

Updated by nobu (Nobuyoshi Nakada) over 10 years ago

(13/07/16 20:37), phluid61 (Matthew Kerwin) wrote:

nobu (Nobuyoshi Nakada) wrote:

phluid61 (Matthew Kerwin) wrote:

I wouldn't complain if there was a kwarg, although I'm not sure which way it should switch: time_obj.elapsed(float: true) or time_obj.elapsed(nanoseconds: true)

time_obj.elapsed(in: :nanoseconds) ?

I had considered that, but I wonder if people aren't going to ask for (or expect) support for :milliseconds, :hours, :days, :aztec_great_cycles, etc. The rules about which symbols are valid seems a little arbitrary.

Your points are all true for {nanoseconds: true} too.

And if support for milliseconds is implemented, what would you expect from elapsed(nanoseconds: true, milliseconds: true)?

And then I suppose the question could become: why are nanoseconds an integer, but seconds a float?

Ditto.

And why doesn't Time#- accept the same kwarg?

Ditto.

And it's because of the operator syntax.

And so on with the bike-shedding. I don't know a good answer for most of these hypothetical questions, so I guess the short-term solution would be to make #elapsed be a short-cut for Time.now - self, and return a float number of seconds.

You propose the method not to take any arguments?
I had supposed that you'd wanted the keyward argument.

Updated by phluid61 (Matthew Kerwin) over 10 years ago

nobu (Nobuyoshi Nakada) wrote:

You propose the method not to take any arguments?
I had supposed that you'd wanted the keyward argument.

Since the discussion has moved towards defining two separate methods {#elapsed => (float)s and #elapsed_nanoseconds => (int)ns} I do prefer a keyword argument {in: :nanoseconds}. Sorry for wavering back and forth on the issue so noisily.

Updated by tenderlovemaking (Aaron Patterson) over 10 years ago

On Wed, Jul 17, 2013 at 01:04:37PM +0900, phluid61 (Matthew Kerwin) wrote:

Issue #8640 has been updated by phluid61 (Matthew Kerwin).

nobu (Nobuyoshi Nakada) wrote:

You propose the method not to take any arguments?
I had supposed that you'd wanted the keyward argument.

Since the discussion has moved towards defining two separate methods {#elapsed => (float)s and #elapsed_nanoseconds => (int)ns} I do prefer a keyword argument {in: :nanoseconds}. Sorry for wavering back and forth on the issue so noisily.

I don't care if a method exists that has kw args, but I would not use
it. The things we need to time in Rails are fairly fast (say 200ms on
the slow side) and happen frequently, which means that object
allocations matter. A kwarg method will end up allocating a hash on
every call.

If someone else wants to make a method with kwargs, I think that's
great, but it's not what I'm pushing for here. My point is to reduce
object allocations. :-)

--
Aaron Patterson
http://tenderlovemaking.com/

Updated by phluid61 (Matthew Kerwin) over 10 years ago

tenderlovemaking (Aaron Patterson) wrote:

On Wed, Jul 17, 2013 at 01:04:37PM +0900, phluid61 (Matthew Kerwin) wrote:

Since the discussion has moved towards defining two separate methods {#elapsed => (float)s and #elapsed_nanoseconds => (int)ns} I do prefer a keyword argument {in: :nanoseconds}. Sorry for wavering back and forth on the issue so noisily.

I don't care if a method exists that has kw args, but I would not use
it. The things we need to time in Rails are fairly fast (say 200ms on
the slow side) and happen frequently, which means that object
allocations matter. A kwarg method will end up allocating a hash on
every call.

If someone else wants to make a method with kwargs, I think that's
great, but it's not what I'm pushing for here. My point is to reduce
object allocations. :-)

In that case, would #8096 be a better proposal? Since that one doesn't even allocate a Time object.

Updated by tenderlovemaking (Aaron Patterson) over 10 years ago

On Thu, Jul 18, 2013 at 07:59:34AM +0900, phluid61 (Matthew Kerwin) wrote:

Issue #8640 has been updated by phluid61 (Matthew Kerwin).

tenderlovemaking (Aaron Patterson) wrote:

On Wed, Jul 17, 2013 at 01:04:37PM +0900, phluid61 (Matthew Kerwin) wrote:

Since the discussion has moved towards defining two separate methods {#elapsed => (float)s and #elapsed_nanoseconds => (int)ns} I do prefer a keyword argument {in: :nanoseconds}. Sorry for wavering back and forth on the issue so noisily.

I don't care if a method exists that has kw args, but I would not use
it. The things we need to time in Rails are fairly fast (say 200ms on
the slow side) and happen frequently, which means that object
allocations matter. A kwarg method will end up allocating a hash on
every call.

If someone else wants to make a method with kwargs, I think that's
great, but it's not what I'm pushing for here. My point is to reduce
object allocations. :-)

In that case, would #8096 be a better proposal? Since that one doesn't even allocate a Time object.

I don't think so. We need subsecond resolution, which (if we used
#8096) would require a possible bignum allocation (from what I gather in
the ticket).

--
Aaron Patterson
http://tenderlovemaking.com/

Updated by rosenfeld (Rodrigo Rosenfeld Rosas) over 10 years ago

Em 18-07-2013 03:03, Aaron Patterson escreveu:

In that case, would #8096 be a better proposal? Since that one doesn't even allocate a Time object.
I don't think so. We need subsecond resolution, which (if we used
#8096) would require a possible bignum allocation (from what I gather in
the ticket).

But I believe he has a point, Aaron. Maybe we should avoid allocating a
time object (Time.now) and instead introduce something like Java's
System.currentTimeMillis():

start = Benchmark.start
operation
report start.since_beginning
other_operation
report start.since_beginning # from start= on
report start.since_last_report # since operation

This new object would be lighter than Time as it would only contain the
start absolute nanoseconds or millis since epoch, maybe.

I haven't given the names much of a thought, but that would be the idea.
Makes sense?

Updated by rosenfeld (Rodrigo Rosenfeld Rosas) over 10 years ago

Also, as a side effect, this new class could have methods like
nanoseconds_ellapsed, and seconds_ellapsed and the like.

Also, it could bookmark some parts of the code. Example:

time_recording = Benchmark.start
some_code
time_recording.bookmark(:a)
more_code
report time_recording.seconds_ellapsed_since(:a)

Updated by phluid61 (Matthew Kerwin) over 10 years ago

rosenfeld (Rodrigo Rosenfeld Rosas) wrote:

Em 18-07-2013 03:03, Aaron Patterson escreveu:

In that case, would #8096 be a better proposal? Since that one doesn't even allocate a Time object.
I don't think so. We need subsecond resolution, which (if we used
#8096) would require a possible bignum allocation (from what I gather in
the ticket).

But I believe he has a point, Aaron. Maybe we should avoid allocating a
time object (Time.now) and instead introduce something like Java's
System.currentTimeMillis():

start = Benchmark.start
operation
report start.since_beginning
other_operation
report start.since_beginning # from start= on
report start.since_last_report # since operation

This new object would be lighter than Time as it would only contain the
start absolute nanoseconds or millis since epoch, maybe.

I haven't given the names much of a thought, but that would be the idea.
Makes sense?

Actually the supplied patch uses clock_gettime(CLOCK_REALTIME), which is already the C equivalent of System.currentTimeMillis(). The only issue is that it allocates a Time object (although it doesn't matter because that happens before the benchmarking commences). Since the getting of the final time and the calculation of the elapsed duration all happen in C-land, there's very little cost involved, even when the numbers are very large.

However, one of the threads of discussion on #8096 suggested using clock_gettime(CLOCK_MONOTONIC)* or System.nanoTime(), which count from an arbitrary epoch (so the number would usually be smaller and thus more likely to fit into a Fixnum). It would be quite light to do:

start = Time.timestamp # clock_gettime(CLOCK_MONOTONIC) => Fixnum
# ... stuff ...
delta = start - Time.timestamp # => Fixnum

..even if you have to do more of the logic yourself.

I'm +0 for the original proposal, just because of the naming/kwarg issue, otherwise I'd be +1.

  • or CLOCK_PROCESS_CPUTIME_ID or CLOCK_THREAD_CPUTIME_ID if they're available, and that's what you need.

Updated by akr (Akira Tanaka) over 10 years ago

2013/7/19 phluid61 (Matthew Kerwin) :

Actually the supplied patch uses clock_gettime(CLOCK_REALTIME), which is already the C equivalent of System.currentTimeMillis(). The only issue is that it allocates a Time object (although it doesn't matter because that happens before the benchmarking commences). Since the getting of the final time and the calculation of the elapsed duration all happen in C-land, there's very little cost involved, even when the numbers are very large.

I feel what we need is Process.clock_gettime, as a primitive.

Tanaka Akira

Updated by rosenfeld (Rodrigo Rosenfeld Rosas) over 10 years ago

You're right, Matthew, although I'd appreciate some bookmark capabilities... But I understand it would be more costly since you'd need to malloc and free the struct everytime a bookmark is created...

Tanaka, I believe most people are interested in elapsed time rather than absolute time (Process.clock_gettime, System.currentTimeMillis(), etc). Having to process the elapsed time in the Ruby side is probably slower than doing the math on the C side, right?

Updated by phluid61 (Matthew Kerwin) over 10 years ago

rosenfeld (Rodrigo Rosenfeld Rosas) wrote:

You're right, Matthew, although I'd appreciate some bookmark capabilities... But I understand it would be more costly since you'd need to malloc and free the struct everytime a bookmark is created...

Tanaka, I believe most people are interested in elapsed time rather than absolute time (Process.clock_gettime, System.currentTimeMillis(), etc). Having to process the elapsed time in the Ruby side is probably slower than doing the math on the C side, right?

clock_gettime allows you to choose which clock to get (on modern Linux, there are four), and if your relative timestamps are both Fixnums the difference between Ruby-land and C-land arithmetic is negligible. See: https://github.com/phluid61/ruby-experiments/tree/master/gettime

Updated by vipulnsward (Vipul Amler) over 10 years ago

+1

Should save a lot of Time object allocations.

Actions #22

Updated by kosaki (Motohiro KOSAKI) about 8 years ago

Actions #23

Updated by hsbt (Hiroshi SHIBATA) about 2 years ago

  • Project changed from 14 to Ruby master
Actions

Also available in: Atom PDF

Like0
Like0Like0Like0Like0Like0Like0Like0Like0Like0Like0Like0Like0Like0Like0Like0Like0Like0Like0Like0Like0Like0Like0Like0