Feature #8640
openAdd Time#elapsed to return nanoseconds since creation
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
Updated by rosenfeld (Rodrigo Rosenfeld Rosas) over 11 years ago
+1
Updated by duerst (Martin Dürst) over 11 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 11 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 11 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 11 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 11 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 11 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 11 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 11 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_nanosecondsI 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
andelapsed_nanoseconds
to matz. :-)
Updated by nobu (Nobuyoshi Nakada) over 11 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 11 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 11 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 11 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 11 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 11 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 11 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 11 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 operationThis 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 11 years ago
2013/7/19 phluid61 (Matthew Kerwin) matthew@kerwin.net.au:
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 11 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 11 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 11 years ago
+1
Should save a lot of Time object allocations.
Updated by kosaki (Motohiro KOSAKI) almost 9 years ago
- Related to Feature #12173: `Time#till_now` added
Updated by hsbt (Hiroshi SHIBATA) almost 3 years ago
- Project changed from 14 to Ruby master