www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - TickDuration deprecation

reply Timon Gehr <timon.gehr gmx.ch> writes:
This is quite annoying:

void main(){
     import std.datetime;
     StopWatch sw;
     import std.stdio;
     writeln(sw.peek().to!("seconds",double));
}

This gives the deprecation warning:
Deprecation: struct std.datetime.StopWatch is deprecated - Use 
std.datetime.stopwatch.StopWatch.

Then, if I do that:

void main(){
     import std.datetime.stopwatch: StopWatch;
     StopWatch sw;
     import std.stdio;
     writeln(sw.peek().to!("seconds",double));
}

Error: no property 'to' for type 'Duration'

This is among the most basic use cases for StopWatch. For example, if I 
want to quickly plot some timing data, I'll invariably want to convert 
times to floating point values of the correct units.

The reason given for this situation is:
https://issues.dlang.org/show_bug.cgi?id=11353

(Jonathan M Davis from comment #4)
 TickDuration will soon be deprecated, and none of the other time stuff
 supports floating point. Anyone who wants that functionality can 
calculate
 the floating point values from the integral values that the types do
 provide. It's not something that most code should be doing, but the API
 makes it possible for those who care.
"Not something that most code should be doing"? I have never used StopWatch and then /not/ wanted to do something like to!("seconds",double)! There seems to be no good reason to break my code beyond requiring a different import here. What are the perceived shortcomings of the to!("seconds", double) interface?
Nov 18
next sibling parent reply Jonathan M Davis <newsgroup.d jmdavisprog.com> writes:
On Saturday, November 18, 2017 15:03:05 Timon Gehr via Digitalmars-d wrote:
 This is quite annoying:

 void main(){
      import std.datetime;
      StopWatch sw;
      import std.stdio;
      writeln(sw.peek().to!("seconds",double));
 }

 This gives the deprecation warning:
 Deprecation: struct std.datetime.StopWatch is deprecated - Use
 std.datetime.stopwatch.StopWatch.

 Then, if I do that:

 void main(){
      import std.datetime.stopwatch: StopWatch;
      StopWatch sw;
      import std.stdio;
      writeln(sw.peek().to!("seconds",double));
 }

 Error: no property 'to' for type 'Duration'

 This is among the most basic use cases for StopWatch. For example, if I
 want to quickly plot some timing data, I'll invariably want to convert
 times to floating point values of the correct units.

 The reason given for this situation is:
 https://issues.dlang.org/show_bug.cgi?id=11353

 (Jonathan M Davis from comment #4)

  > TickDuration will soon be deprecated, and none of the other time stuff
  > supports floating point. Anyone who wants that functionality can

 calculate

  > the floating point values from the integral values that the types do
  > provide. It's not something that most code should be doing, but the API
  > makes it possible for those who care.

 "Not something that most code should be doing"? I have never used
 StopWatch and then /not/ wanted to do something like
 to!("seconds",double)!

 There seems to be no good reason to break my code beyond requiring a
 different import here. What are the perceived shortcomings of the
 to!("seconds", double) interface?
core.time and std.datetime avoid floating point math, because using it introduces errors into the calculations. It does make some sense to generate a floating point value if all you're looking to do is print it out at the end, but using floating point values in the calculations is a source of bugs - e.g. converting between clock frequencies using floating point values would be particularly bad, much as it's the obvious first implementation; core.time.convClockFreq does it all with integral math and avoids the problem and even manages to support a higher range of values in the process than it would if it were implemented with double or real. As such, I've avoided floating point values like the plague in the time stuff. The only reason that TickDuration does it at all is because it was written by someone else, and it has various design flaws that mean that it really should never have been in core.time in the first place (most notably that it conflates a point in time of the monotonic clock a a duration of the monotonic clock, making it really easy to screw up when using it). So, we've slowly been moving towards getting rid of it in favor of using MonoTime and Duration. Printing out a floating point value for something like the number of seconds can make sense, but using floating point to do math with time really doesn't. And it's trivial enough to do the math yourself to get the number of seconds (or milliseconds or minutes or whatever) as a floating point value if you really want that that I'd much rather not add it to core.time, because that would just be encouraging folks to start using floating point values for something other than simply printing out the value, which will lead to buggy code which could have easily been avoided just by sticking to integral math. Folks have asked for the ability to create Durations from floating point values too, and I rejected that for the same reason - using floating point values with time is just begging for bugs, and Walter backed me up on that one. - Jonathan M Davis
Nov 18
next sibling parent bauss <jj_1337 live.dk> writes:
On Saturday, 18 November 2017 at 16:17:00 UTC, Jonathan M Davis 
wrote:
 On Saturday, November 18, 2017 15:03:05 Timon Gehr via 
 Digitalmars-d wrote:
 [...]
core.time and std.datetime avoid floating point math, because using it introduces errors into the calculations. It does make some sense to generate a floating point value if all you're looking to do is print it out at the end, but using floating point values in the calculations is a source of bugs - e.g. converting between clock frequencies using floating point values would be particularly bad, much as it's the obvious first implementation; core.time.convClockFreq does it all with integral math and avoids the problem and even manages to support a higher range of values in the process than it would if it were implemented with double or real. [...]
If you ask me, then time durations doesn't make sense as anything else other than signed integers. I can see that you can do something like "0.5" for half a second, but really you shouldn't be doing the duration in seconds then, but rather in milliseconds and pass 500 for half a second. So please, I hope it never gets added. That's just my two cents. Especially seeing how flawed floating points are.
Nov 18
prev sibling next sibling parent reply Jon Degenhardt <jond noreply.com> writes:
On Saturday, 18 November 2017 at 16:17:00 UTC, Jonathan M Davis 
wrote:
 On Saturday, November 18, 2017 15:03:05 Timon Gehr via 
 Digitalmars-d wrote:

 [snip]

 Printing out a floating point value for something like the 
 number of seconds can make sense, but using floating point to 
 do math with time really doesn't. And it's trivial enough to do 
 the math yourself to get the number of seconds (or milliseconds 
 or minutes or whatever) as a floating point value if you really 
 want that that I'd much rather not add it to core.time, because 
 that would just be encouraging folks to start using floating 
 point values for something other than simply printing out the 
 value, which will lead to buggy code which could have easily 
 been avoided just by sticking to integral math.
When I use timers to measure time durations, whether for program performance or something else, at the end I usually want to convert to units of time appropriate for what is being measured and print it in a format easily consumable by human readers. That typically means some fixed precision floating point format. I admittedly don't understand the argument that it should be hard to user programs to convert time durations to alternate standard units of measure. But even so, it would seem to make sense for the documentation of std.datetime.stopwatch to provide clear examples of printing a duration in different standard time units. In documentation's current form, it takes quite a bit of digging to figure out how to do this. I'd recommend at least making it clear in the documentation how to do this. Yes, this would also make it easier for users to convert to float, but printing in standard units of measure is a rather basic operation.
Nov 18
next sibling parent reply Rumbu <rumbu rumbu.ro> writes:
On Saturday, 18 November 2017 at 22:46:20 UTC, Jon Degenhardt 
wrote:

 I admittedly don't understand the argument that it should be 
 hard to user programs to convert time durations to alternate 
 standard units of measure. But even so, it would seem to make 
 sense for the documentation of std.datetime.stopwatch to 
 provide clear examples of printing a duration in different 
 standard time units. In documentation's current form, it takes 
 quite a bit of digging to figure out how to do this. I'd 
 recommend at least making it clear in the documentation how to 
 do this. Yes, this would also make it easier for users to 
 convert to float, but printing in standard units of measure is 
 a rather basic operation.
+1. I had the same problem with some benchmark intensive project. It took me some time to resolve the deprecations, but I lost more time diging for a way to display times in a human readable form. Internal representations of durations are useless if you cannot display them in a standard unit of measure.
Nov 19
parent reply Jonathan M Davis <newsgroup.d jmdavisprog.com> writes:
On Sunday, November 19, 2017 15:01:50 Rumbu via Digitalmars-d wrote:
 On Saturday, 18 November 2017 at 22:46:20 UTC, Jon Degenhardt

 wrote:
 I admittedly don't understand the argument that it should be
 hard to user programs to convert time durations to alternate
 standard units of measure. But even so, it would seem to make
 sense for the documentation of std.datetime.stopwatch to
 provide clear examples of printing a duration in different
 standard time units. In documentation's current form, it takes
 quite a bit of digging to figure out how to do this. I'd
 recommend at least making it clear in the documentation how to
 do this. Yes, this would also make it easier for users to
 convert to float, but printing in standard units of measure is
 a rather basic operation.
+1. I had the same problem with some benchmark intensive project. It took me some time to resolve the deprecations, but I lost more time diging for a way to display times in a human readable form. Internal representations of durations are useless if you cannot display them in a standard unit of measure.
Was the documentation on Duration not informative enough, or did you have trouble finding it from the documentation for the benchmarking functions, or something else? Simply converting a Duration to a string gives you what I would have thought would have been plenty human readable (though obviously not necessarily what you want in all cases), and it's split and total functions should make it straightforward to put the result in some other format if that's what you want. Was the documentation on those not enough? - Jonathan M Davis
Nov 19
next sibling parent reply Rumbu <rumbu rumbu.ro> writes:
On Sunday, 19 November 2017 at 16:02:34 UTC, Jonathan M Davis 
wrote:

 Was the documentation on Duration not informative enough, or 
 did you have trouble finding it from the documentation for the 
 benchmarking functions, or something else? Simply converting a 
 Duration to a string gives you what I would have thought would 
 have been plenty human readable (though obviously not 
 necessarily what you want in all cases), and it's split and 
 total functions should make it straightforward to put the 
 result in some other format if that's what you want. Was the 
 documentation on those not enough?

 - Jonathan M Davis
The documentation on Duration doesn't specify any method of converting it to usecs (that's what I need). TickDuration has a usecs member, I really don't care how many ticks are there.
Nov 19
parent reply Jonathan M Davis <newsgroup.d jmdavisprog.com> writes:
On Sunday, November 19, 2017 16:55:06 Rumbu via Digitalmars-d wrote:
 On Sunday, 19 November 2017 at 16:02:34 UTC, Jonathan M Davis

 wrote:
 Was the documentation on Duration not informative enough, or
 did you have trouble finding it from the documentation for the
 benchmarking functions, or something else? Simply converting a
 Duration to a string gives you what I would have thought would
 have been plenty human readable (though obviously not
 necessarily what you want in all cases), and it's split and
 total functions should make it straightforward to put the
 result in some other format if that's what you want. Was the
 documentation on those not enough?

 - Jonathan M Davis
The documentation on Duration doesn't specify any method of converting it to usecs (that's what I need). TickDuration has a usecs member, I really don't care how many ticks are there.
Duration doesn't have any ticks. As its documentation states, it holds its time internally in hecto-nanoseconds. Its total function can be used to convert the entire Duration to whatever units you want: https://dlang.org/phobos/core_time.html#.Duration.total whereas its split function allows you to split it into whatever units you want: https://dlang.org/phobos/core_time.html#.Duration.split And simply converting a Duration to a string prints out the units with them split out in a human readable manner. e.g. writeln(usecs(502397292)); would print 8 minutes, 22 secs, 397 ms, and 292 μs https://dlang.org/phobos/core_time.html#.Duration.toString Subtracting MonoTimes specifically gives a Duration rather than a TickDuration or anything else representing the number of ticks, because you don't normally care about the number of ticks, and its cleaner to have only one duration type. Anyone who wants the number of ticks would have to use MonoTime's ticks property to get at them and then subtract those directly. So, anything holding the monotonic time now uses MonoTime to hold the times and gives the length of time as a Duration. - Jonathan M Davis
Nov 19
parent reply Rumbu <rumbu rumbu.ro> writes:
On Sunday, 19 November 2017 at 17:26:04 UTC, Jonathan M Davis 
wrote:

 Duration doesn't have any ticks. As its documentation states, 
 it holds its time internally in hecto-nanoseconds. Its total 
 function can be used to convert the entire Duration to whatever 
 units you want:

 https://dlang.org/phobos/core_time.html#.Duration.total

 whereas its split function allows you to split it into whatever 
 units you want:

 https://dlang.org/phobos/core_time.html#.Duration.split

 And simply converting a Duration to a string prints out the 
 units with them split out in a human readable manner. e.g.

     writeln(usecs(502397292));

 would print

 8 minutes, 22 secs, 397 ms, and 292 μs

 https://dlang.org/phobos/core_time.html#.Duration.toString

 Subtracting MonoTimes specifically gives a Duration rather than 
 a TickDuration or anything else representing the number of 
 ticks, because you don't normally care about the number of 
 ticks, and its cleaner to have only one duration type. Anyone 
 who wants the number of ticks would have to use MonoTime's 
 ticks property to get at them and then subtract those directly.

 So, anything holding the monotonic time now uses MonoTime to 
 hold the times and gives the length of time as a Duration.

 - Jonathan M Davis
Thank you Jonathan for the effort of explaini g, better put all this in the docs. Believe me, total is not the first word that comes to my mind when I want to use a conversion. More than that, total is documented on another page. The documentation is lost in details like internal representation (I don't care), mono time (I don't even want to know what is this), operators (I don't see the point of documenting internals) instead of highlighting the main usage. Now put yourself in the user's shoes. You had something like x.usecs in your code, now you get a deprecation message. You resolve the deprecation, but the compiler complains: On Sunday, 19 November 2017 at 17:26:04 UTC, Jonathan M Davis wrote:
 Duration doesn't have any ticks. As its documentation states, 
 it holds its time internally in hecto-nanoseconds. Its total 
 function can be used to convert the entire Duration to whatever 
 units you want:

 https://dlang.org/phobos/core_time.html#.Duration.total

 whereas its split function allows you to split it into whatever 
 units you want:

 https://dlang.org/phobos/core_time.html#.Duration.split

 And simply converting a Duration to a string prints out the 
 units with them split out in a human readable manner. e.g.

     writeln(usecs(502397292));

 would print

 8 minutes, 22 secs, 397 ms, and 292 μs

 https://dlang.org/phobos/core_time.html#.Duration.toString

 Subtracting MonoTimes specifically gives a Duration rather than 
 a TickDuration or anything else representing the number of 
 ticks, because you don't normally care about the number of 
 ticks, and its cleaner to have only one duration type. Anyone 
 who wants the number of ticks would have to use MonoTime's 
 ticks property to get at them and then subtract those directly.

 So, anything holding the monotonic time now uses MonoTime to 
 hold the times and gives the length of time as a Duration.

 - Jonathan M Davis
Thank you Jonathan for the effort of explaini g, better put all this in the docs. Believe me, total is not the first word that comes to my mind when I want to use a conversion. More than that, total is documented on another page. The documentation is lost in details like internal representation (I don't care), mono time (I don't even want to know what is this), operators (I don't see the point of documenting internals) instead of highlighting the main usage. Now put yourself in the phobos user's shoes. You had something like sometickdurationresult.usecs in your code, now you get a deprecation message. I skip the part where I must write now my own benchmarkig functions because someone had the brilliant idea to deprecate them too (why?). Finally, you resolve the deprecation, but the compiler throws an error like this: core.time.dur!"usecs".dur (long length) is not callable using argument types (Duration) Based on the error above, how in the world can I deduce that I must use something like "total"? Note: I understood what happens, I'm just playing the devil's advocate here :)
Nov 19
parent Jonathan M Davis <newsgroup.d jmdavisprog.com> writes:
On Sunday, November 19, 2017 17:56:37 Rumbu via Digitalmars-d wrote:
 Finally, you resolve the deprecation, but the compiler throws an
 error like this:

 core.time.dur!"usecs".dur (long length) is not callable using
 argument types (Duration)

 Based on the error above, how in the world can I deduce that I
 must use something like "total"?

 Note: I understood what happens, I'm just playing the devil's
 advocate here :)
I would expect you to go read the documentation for Duration if you don't already know how to use Duration. - Jonathan M Davis
Nov 19
prev sibling parent reply Jon Degenhardt <jond noreply.com> writes:
On Sunday, 19 November 2017 at 16:02:34 UTC, Jonathan M Davis 
wrote:
 On Sunday, November 19, 2017 15:01:50 Rumbu via Digitalmars-d 
 wrote:
 On Saturday, 18 November 2017 at 22:46:20 UTC, Jon Degenhardt
 Was the documentation on Duration not informative enough, or 
 did you have trouble finding it from the documentation for the 
 benchmarking functions, or something else? Simply converting a 
 Duration to a string gives you what I would have thought would 
 have been plenty human readable (though obviously not 
 necessarily what you want in all cases), and it's split and 
 total functions should make it straightforward to put the 
 result in some other format if that's what you want. Was the 
 documentation on those not enough?
1) Finding the documentation for Duration from the documentation of std.datetime.stopwatch is not quick enough. There is only one link from the page, and it's near the bottom, with the benchmark function (which is rarely what I need). Also, aside from the link at the bottom of the page, no mention of what module it is in. 2) The stopwatch page doesn't describe a Duration, and it also refers to core.time.MonoTime, plus a discussion that the "precision of StopWatch differs from system to system. And references to the depreciated TickDuration Before one can do anything, one has to parse through all this material. It's all necessary material for tasks requiring very high accuracy. However, if what is being timed is inherently precise only to hundreds or tens of milliseconds, then this is way more investigation than needs to be done for the task at hand. 3) hecto-nanoseconds is not an especially common unit of measure. 4) Even from the Duration documentation, there aren't many examples of conversion to other standard units of measure. The only one I see is converting 12 days to hecto-nanoseconds, which isn't helpful as an example. 5) The built in conversion to string is one I've never found useful. Generally, I'm putting a set a values in a table, perhaps a large table. The output shown is not amenable to this. And yes, it often needs to be both human and machine readable. For example, I may read the table into R and plot the values. Some recommendations: a) Put a link to the Duration documentation in StopWatch page. b) Put examples in the StopWatch and Duration sections that explicitly show how to convert to usecs, milliseconds, and perhaps seconds in floating point formats. Show this in the context of printing them. c) Try to clean up some of the language describing the backward compatibility vs the newer stuff. It's a bit intertwined. The language doesn't have to explain the depreciation or justify it, at least not mixed with the description of the new facilities. d) Be more explicit in the documentation about tradeoffs of integer precision used in Duration and floating point accuracy. That Duration supports this high accuracy without loss of precision in the face of mathematical operations is a real value, one worth calling out. However, it's also the case that this high mathematical precision is not required for many timing use cases, especially in the printing, but even calculations. Making this distinction puts the stopwatch facilities more in the position of being an enabler of good end-user choices and less trying to prevent end-user mistakes. The reality is, that the accuracy needs are only known in the context of the timing measurements being done. The library code does not have enough information to know. However, providing the user with the information to make good choices about accuracy representation is a real benefit.
Nov 19
next sibling parent Timon Gehr <timon.gehr gmx.ch> writes:
On 19.11.2017 20:29, Jon Degenhardt wrote:
 ...
 
 Some recommendations:
 
 a) Put a link to the Duration documentation in StopWatch page.
 
 b) Put examples in the StopWatch and Duration sections that explicitly 
 show how to convert to usecs, milliseconds, and perhaps seconds in 
 floating point formats. Show this in the context of printing them.
 
 c) Try to clean up some of the language describing the backward 
 compatibility vs the newer stuff. It's a bit intertwined. The language 
 doesn't have to explain the depreciation or justify it, at least not 
 mixed with the description of the new facilities.
 
 d) Be more explicit in the documentation about tradeoffs of integer 
 precision used in Duration and floating point accuracy. That Duration 
 supports this high accuracy without loss of precision in the face of 
 mathematical operations is a real value, one worth calling out. However, 
 it's also the case that this high mathematical precision is not required 
 for many timing use cases, especially in the printing, but even 
 calculations.
 
 Making this distinction puts the stopwatch facilities more in the 
 position of being an enabler of good end-user choices and less trying to 
 prevent end-user mistakes. The reality is, that the accuracy needs are 
 only known in the context of the timing measurements being done. The 
 library code does not have enough information to know. However, 
 providing the user with the information to make good choices about 
 accuracy representation is a real benefit.
This is exactly how I think about this. I'd additionally recommend to keep supporting something like "to". It's nicer to read than something like total!"hnsecs"/1e7.
Nov 20
prev sibling next sibling parent bauss <jj_1337 live.dk> writes:
On Sunday, 19 November 2017 at 19:29:06 UTC, Jon Degenhardt wrote:
 On Sunday, 19 November 2017 at 16:02:34 UTC, Jonathan M Davis 
 wrote:
 [...]
 [...]
1) Finding the documentation for Duration from the documentation of std.datetime.stopwatch is not quick enough. There is only one link from the page, and it's near the bottom, with the benchmark function (which is rarely what I need). Also, aside from the link at the bottom of the page, no mention of what module it is in. [...]
Considering you know what would improve the documentations, why not create a pull-request yourself?
Nov 20
prev sibling parent Kagamin <spam here.lot> writes:
On Sunday, 19 November 2017 at 19:29:06 UTC, Jon Degenhardt wrote:
 1) Finding the documentation for Duration from the 
 documentation of std.datetime.stopwatch is not quick enough. 
 There is only one link from the page, and it's near the bottom, 
 with the benchmark function (which is rarely what I need).
dpldocs sort of allow you to do it: http://dpldocs.info/experimental-docs/std.datetime.stopwatch StopWatch.peek.html it links types used in function signature.
Nov 21
prev sibling parent Kagamin <spam here.lot> writes:
On Saturday, 18 November 2017 at 22:46:20 UTC, Jon Degenhardt 
wrote:
 I admittedly don't understand the argument that it should be 
 hard to user programs to convert time durations to alternate 
 standard units of measure.
If you want to communicate with a system that uses a floating point time format, you would need exactly same conversion algorithm not just something similar, so that round trip doesn't introduce computational errors.
Nov 21
prev sibling next sibling parent qznc <qznc web.de> writes:
On Saturday, 18 November 2017 at 16:17:00 UTC, Jonathan M Davis 
wrote:
 Folks have asked for the ability to create Durations from 
 floating point values too, and I rejected that for the same 
 reason - using floating point values with time is just begging 
 for bugs, and Walter backed me up on that one.
Makes me think about my money library. It does accept floating point values and it has lead to at least one confused user [0]. Hm. [0] https://github.com/qznc/d-money/issues/3
Nov 19
prev sibling parent reply Walter Bright <newshound2 digitalmars.com> writes:
On 11/18/2017 8:17 AM, Jonathan M Davis wrote:
 Folks have asked for the ability to create Durations from floating point
 values too, and I rejected that for the same reason - using floating point
 values with time is just begging for bugs, and Walter backed me up on that
 one.
Yup. It's the same reason one does not do accounting with floating point. Computer clocks have discrete ticks, they are not continuous. The behavior maps cleanly onto integral math, not fuzzy fp math.
Nov 21
parent reply Timon Gehr <timon.gehr gmx.ch> writes:
On 21.11.2017 21:52, Walter Bright wrote:
 On 11/18/2017 8:17 AM, Jonathan M Davis wrote:
 Folks have asked for the ability to create Durations from floating point
 values too, and I rejected that for the same reason - using floating 
 point
 values with time is just begging for bugs, and Walter backed me up on 
 that
 one.
Yup. It's the same reason one does not do accounting with floating point. ...
The use case here is plotting the time taken by an algorithm depending on instance size.
 Computer clocks have discrete ticks, they are not continuous.
That may be true, but the plotting library may still just expect a list of doubles. What's the point of removing the simple conversion function that was already available for such use cases? This is a breaking change with zero benefits.
 The behavior maps cleanly onto integral math,
I'm not doing computations on times. I could just use Duration for that.
 not fuzzy fp math.
 
There is nothing fuzzy about floating point operations, but still, yes, for some use cases, the tiny rounding error will just not matter.
Nov 21
parent reply Walter Bright <newshound2 digitalmars.com> writes:
On 11/21/2017 1:40 PM, Timon Gehr wrote:
 Computer clocks have discrete ticks, they are not continuous.
That may be true, but the plotting library may still just expect a list of doubles. What's the point of removing the simple conversion function that was already available for such use cases? This is a breaking change with zero benefits.
I'm in general opposed to "kitchen sink" abstractions, preferring pluggable component abstractions. Floating point has no business being in a type that is represented as an integral type. There are no 0.3 clock ticks, the notion does not exist.
 The behavior maps cleanly onto integral math,
I'm not doing computations on times. I could just use Duration for that.
Time durations are always discrete quanta by the nature of the clock used.
 not fuzzy fp math.
There is nothing fuzzy about floating point operations,
Fuzzy as in inexact. Integral time computations are always an exact multiple of clock ticks.
 but still, yes, for some 
 use cases, the tiny rounding error will just not matter.
Whether the rounding error matters or not should not be decided by the time package, it should be decided by what the user decides to do with the time values. I.e. it is not properly part of the time abstraction.
Nov 21
next sibling parent reply Walter Bright <newshound2 digitalmars.com> writes:
There is another, perhaps obsolete, consideration.

Some CPUs do not have floating point units. C, for example, is carefully set up 
so that when you don't need floating point, it isn't required to have an FPU. 
This made C usable on cheap processors.

It's sorta like "why is the GC linked in even though I never used it?"
Nov 21
next sibling parent reply Jon Degenhardt <jond noreply.com> writes:
On Wednesday, 22 November 2017 at 05:50:53 UTC, Walter Bright 
wrote:
 There is another, perhaps obsolete, consideration.

 Some CPUs do not have floating point units. C, for example, is 
 carefully set up so that when you don't need floating point, it 
 isn't required to have an FPU. This made C usable on cheap 
 processors.

 It's sorta like "why is the GC linked in even though I never 
 used it?"
Hi Walter - I wonder if there is a miscommunication. My understanding is that the question is whether there should be a built-in conversion from Duration to float/double in a specific unit of measure, like milliseconds. It sounds as if your concern is more to ensure that the time package not be required to support something other than integral values in its internal operations. Perhaps there is an alternative perspective, but being able to convert a Duration to a double in a specific unit of measure would not seem to place any burden on the time package to support the result of conversion as a first class time object. In this view, what happens with the converted value is entirely in the hands of the end user, not the time package. If the user decides to use the double in a mathematical operation, floating point round off error is the responsibility of the user, not the time package. To give a concrete example of why this is conversion is useful - I often do performance analysis involving service calls that take from single digit milliseconds to hundreds of milliseconds, with standard deviations in that ballpark. I might use tens of thousands of timing samples and analyze those samples using stats and graphing packages. Those packages all use doubles, and it is generally convenient to work in units appropriate for the measurements being done, milliseconds in the case I described. In this case, the last step in generating a timing value is to convert to milliseconds as a double and store it somewhere, often a log file. These will be read back in by the stats/graphing package, where follow-up processing will be done. In the older version of StopWatch, this last step conversion could be done with a call like: double ms = sw.peek.to!("msecs", double)); In the new version, a call needs to be something like: double ms = sw.peek.total!("hnsecs").to!double / 1e+04); The latter form is more error prone and less clear about intent, etc. It sounds as the rationale for depreciating the previous form of conversion is because the end user may incur floating point round-off error by performing mathematical operations on the double value. The user can still perform the conversion, but must go to greater effort. It sounds as if the other part of the rationale is that conversion is likely to be rare. In my experience, this is not the case.
Nov 21
parent Walter Bright <newshound2 digitalmars.com> writes:
On 11/21/2017 11:48 PM, Jon Degenhardt wrote:
 Hi Walter - I wonder if there is a miscommunication. My understanding is that 
 the question is whether there should be a built-in conversion from Duration to 
 float/double in a specific unit of measure, like milliseconds. It sounds as if 
 your concern is more to ensure that the time package not be required to
support 
 something other than integral values in its internal operations.
My perspective is that the time package should not deal with floating point at all. I understand that it is useful in some circumstances to treat them as floating point values, but this should be a layer added on by the user, not by the time package.
 In the older version of StopWatch, this last step conversion could be done
with 
 a call like:
 
      double ms = sw.peek.to!("msecs", double));
That puts the logic of the double conversion into the time package.
 In the new version, a call needs to be something like:
 
      double ms = sw.peek.total!("hnsecs").to!double / 1e+04);
 
 The latter form is more error prone and less clear about intent, etc.
I agree. It is a prime candidate for further encapsulating in a function, such as: /* Returns: duration in milliseconds to 4 digits past the decimal point */ double swAsDouble(StopWatch sw) { return sw.peek.total!("hnsecs").to!double / 1e+04); } double ms = swAsDouble(sw); This function, being a trivial one liner, doesn't really need to be in Phobos. It could be suitable for inclusion in the time package documentation as an example on how to get results in a floating point manner. [As a general philosophy, I oppose Phobos being filled with one liners, a mile wide and an inch deep. Phobos should be a small number of non-obvious, orthogonal, deep functions. As an extreme example, int add2(int i){return i+2;} should not be in Phobos.]
 It sounds 
 as the rationale for depreciating the previous form of conversion is because
the 
 end user may incur floating point round-off error by performing mathematical 
 operations on the double value. The user can still perform the conversion, but 
 must go to greater effort. It sounds as if the other part of the rationale is 
 that conversion is likely to be rare. In my experience, this is not the case.
It is not the rationale I would use for this case. It's also true that floating point operations are a minefield with respect to precision and roundoff decisions. These sorts of decisions should not even be made by the time package, because they will always be wrong for some users, so they should rightfully be pushed to the user.
Nov 22
prev sibling parent reply Timon Gehr <timon.gehr gmx.ch> writes:
On 22.11.2017 06:50, Walter Bright wrote:
 There is another, perhaps obsolete, consideration.
 
 Some CPUs do not have floating point units. C, for example, is carefully 
 set up so that when you don't need floating point, it isn't required to 
 have an FPU. This made C usable on cheap processors.
 
 It's sorta like "why is the GC linked in even though I never used it?"
Why would the conversion function be linked in if I never use it?
Nov 22
parent reply Walter Bright <newshound2 digitalmars.com> writes:
On 11/22/2017 1:41 AM, Timon Gehr wrote:
 Why would the conversion function be linked in if I never use it?
Good question. It depends on how the code is written, and how the compiler represents it in the object file, and how the linker deals with unreferenced parts of object files. `format`, for example, dynamically decides whether to use floating point or not, so it is always linked in.
Nov 22
parent reply Walter Bright <newshound2 digitalmars.com> writes:
On 11/22/2017 2:45 AM, Walter Bright wrote:
 On 11/22/2017 1:41 AM, Timon Gehr wrote:
 Why would the conversion function be linked in if I never use it?
Good question. It depends on how the code is written, and how the compiler represents it in the object file, and how the linker deals with unreferenced parts of object files.
For another example, unreferenced virtual functions never get elided because a reference to them does exist - they're in the virtual function pointer table. And then, of course, everything that virtual function references is never elided. Another reason to be careful of "kitchen sink" abstractions.
Nov 22
parent Jacob Carlborg <doob me.com> writes:
On 2017-11-22 22:41, Walter Bright wrote:

 For another example, unreferenced virtual functions never get elided 
 because a reference to them does exist - they're in the virtual function 
 pointer table. And then, of course, everything that virtual function 
 references is never elided.
Yeah, that's one of the reason why the original Objective-C/D bridge resulted in a 60 MB Hello World executable. At that point we realized we need to add direct support in the compiler for linking with Objective-C. -- /Jacob Carlborg
Nov 23
prev sibling parent reply Timon Gehr <timon.gehr gmx.ch> writes:
On 22.11.2017 03:22, Walter Bright wrote:
 On 11/21/2017 1:40 PM, Timon Gehr wrote:
 Computer clocks have discrete ticks, they are not continuous.
That may be true, but the plotting library may still just expect a list of doubles. What's the point of removing the simple conversion function that was already available for such use cases? This is a breaking change with zero benefits.
I'm in general opposed to "kitchen sink" abstractions, preferring pluggable component abstractions.
But this is orthogonal to my complaint. I don't care which Phobos module contains the conversion functionality. It can be part of std.conv.to, for example.
 Floating point has no business being 
 in a type that is represented as an integral type.
Why would it need to be part of the type? I just want the obvious conversion functionality, to enable further processing where this is appropriate.
 There are no 0.3 clock ticks, the notion does not exist.
 ...
(Great then. There also is no 0.3 float value. :P)
 
 The behavior maps cleanly onto integral math,
I'm not doing computations on times. I could just use Duration for that.
Time durations are always discrete quanta by the nature of the clock used. ...
The plotter or whatever other component consumes the timing data might not care about this.
 
 not fuzzy fp math.
There is nothing fuzzy about floating point operations,
Fuzzy as in inexact.
The result is well-defined, it's just rounded. Whether that is exact or not depends on what you expected the result to be, it's not properly part of the floating point abstraction.
 Integral time computations are always an exact 
 multiple of clock ticks.
 ...
The reason why floating point values are popular is that it is often enough infeasible or unnecessary to do the computation without rounding. The output of a computation might be inexact even if the inputs are not. There needs to be some way to move from one regime to the other.
 
 but still, yes, for some use cases, the tiny rounding error will just 
 not matter.
Whether the rounding error matters or not should not be decided by the time package, it should be decided by what the user decides to do with the time values. I.e. it is not properly part of the time abstraction.
My claim is not that conversion from time to floating point values associated with a few natural units is "properly part of the time abstraction", just that it should exist. Do you agree with that?
Nov 22
parent reply Walter Bright <newshound2 digitalmars.com> writes:
On 11/22/2017 1:38 AM, Timon Gehr wrote:
 My claim is not that conversion from time to floating point values associated 
 with a few natural units is "properly part of the time abstraction", just that 
 it should exist. Do you agree with that?
I refer to my reply to Jon Degenhardt which has a substantial answer to that.
Nov 22
next sibling parent Daniel Kozak <kozzi11 gmail.com> writes:
Ok I understand why there is no conversion from Duration to float, but
would be possible to make Duration.total not a member function? So insted
of:

static mytotal(string unit)(Duration dur)
{
    return dur.total!unit;
}

alias msecs = mytotal!"msecs";

I could just add
alias msecs = total!"msecs";


On Wed, Nov 22, 2017 at 11:48 AM, Walter Bright via Digitalmars-d <
digitalmars-d puremagic.com> wrote:

 On 11/22/2017 1:38 AM, Timon Gehr wrote:

 My claim is not that conversion from time to floating point values
 associated with a few natural units is "properly part of the time
 abstraction", just that it should exist. Do you agree with that?
I refer to my reply to Jon Degenhardt which has a substantial answer to that.
Nov 22
prev sibling parent Jonathan M Davis <newsgroup.d jmdavisprog.com> writes:
On Wednesday, November 22, 2017 12:45:01 Daniel Kozak via Digitalmars-d 
wrote:
 Ok I understand why there is no conversion from Duration to float, but
 would be possible to make Duration.total not a member function? So insted
 of:

 static mytotal(string unit)(Duration dur)
 {
     return dur.total!unit;
 }

 alias msecs = mytotal!"msecs";

 I could just add
 alias msecs = total!"msecs";
How would that help anything? You can clearly create an msecs alias right now, and you could get the same effect using a free function named msecs instead of an alias. Also, core.time.msecs is already an alias for core.time.dur!"msecs", so creating your own symbol called msecs (be it an alias or a free function) could muck with your use of the one from core.time (though it's certainly possible to disambiguate between the two or to use dur!"msecs" directly). And regardless of this specific situation, in general, turning a member function into a free function risks breaking code, since instead of it being a member function that automatically wins out with UFCS, it could suddenly be in conflict with another function of the same name, meaning that the calling code would have to then disambiguate between them whereas it didn't before. So, in general, turning member functions into free functions isn't a great idea if you're not in control over all of the code involved or otherwise aren't concerned about the potential fallout. - Jonathan M Davis
Nov 22
prev sibling parent reply Steven Schveighoffer <schveiguy yahoo.com> writes:
On 11/18/17 9:03 AM, Timon Gehr wrote:

 (Jonathan M Davis from comment #4)
  > TickDuration will soon be deprecated, and none of the other time stuff
  > supports floating point. Anyone who wants that functionality can 
 calculate
  > the floating point values from the integral values that the types do
  > provide. It's not something that most code should be doing, but the API
  > makes it possible for those who care.
 
 
 "Not something that most code should be doing"? I have never used 
 StopWatch and then /not/ wanted to do something like to!("seconds",double)!
 
 There seems to be no good reason to break my code beyond requiring a 
 different import here. What are the perceived shortcomings of the 
 to!("seconds", double) interface?
My take on this, as someone who has argued extensively NOT to use double for timing calculations (I was responsible for adding the TimeSpan type in Tango (the equivalent of Duration) and marginalizing Interval (based on double) [1]): 1. All OS calls with timing requirements use non-floating point to represent how long to sleep. After all a CPU uses discrete math, and the timing implementation is no different. 2. Sometimes it is VERY important to use exact representation for everything. Example: I want to sleep for 1 microsecond, if you had a function that accepts a floating point value, and you pass in 0.000_001, you might actually sleep for 0 ticks, which is vastly different. 3. Sometimes it is not as important. Example: if you want to sleep for 1.75 seconds, having a function that converts the float representation of 1.75 into a Duration is useful, and reasonably accurate. It isn't going to ruin your application if the sleep passed to the OS is 1.749999999 seconds. Your sleep probably isn't going to be exactly accurate anyways, as most of use use non-real-time OSes. 4. There are many libraries, the most obvious one to me is Apple's core library, which use a double for representing time everywhere. So it's not without precedent. 5. Responding to Walter's one-liner resistance, if the one liner is trivial to get right, then sure, don't include it. It could even be written in-line. But if it's easy to get WRONG, or is annoying to have to write, then I think it's worth having, even if it's a one-liner. In my opinion, type conversion is one of those things that falls into that second category. How can I construct the most accurate Duration with a given double that is in the form of seconds? You'd have to know the representation of Duration as hectonanoseconds in order to get this right. While trivial to write once you *know* that, it's not trivial to write if you *don't*. Having a library function (even a one-liner) that says "I'll save you from the implementation details" is useful. related: https://github.com/dlang/druntime/pull/1927 Bottom line: one-liner-ness shouldn't be an automatic disqualifier. ----------- After having used Apple's SDK quite a bit, I've changed my opinion slightly since my Tango days. It is useful to have a floating point representation of time, and can be used to good effect. I would be fine with a mechanism to convert to/from double with a Duration in druntime (in fact, Tango kept this), but still want to have the representation be as accurate as possible when it comes to calling OS functions. All the public APIs of druntime/phobos should take/return only Durations, not doubles, and let the user convert if they want to use doubles elsewhere. -Steve [1] http://dsource.org/projects/tango/ticket/671
Nov 22
parent reply Walter Bright <newshound2 digitalmars.com> writes:
On 11/22/2017 5:45 AM, Steven Schveighoffer wrote:
 1. All OS calls with timing requirements use non-floating point to represent
how 
 long to sleep. After all a CPU uses discrete math, and the timing
implementation 
 is no different.
Microsoft has numerous COM APIs for time functions. One of them I had to deal with used doubles to represent seconds. This had to be interfaced with other time functions that used integers. The trouble came from round trips - there were cases where double=>integer=>double did not produce the same result. Even worse, double=>integer=>double=>integer=>double ... produced a creeping shift in the value! It took much time to come up with a method of preventing such drift, and even that was unreliable.
 5. Responding to Walter's one-liner resistance, if the one liner is trivial to 
 get right, then sure, don't include it. It could even be written in-line. But
if 
 it's easy to get WRONG, or is annoying to have to write, then I think it's
worth 
 having, even if it's a one-liner.
 
     In my opinion, type conversion is one of those things that falls into
that 
 second category. How can I construct the most accurate Duration with a given 
 double that is in the form of seconds? You'd have to know the representation
of 
 Duration as hectonanoseconds in order to get this right. While trivial to
write 
 once you *know* that, it's not trivial to write if you *don't*. Having a
library 
 function (even a one-liner) that says "I'll save you from the implementation 
 details" is useful.
Another solution is to put the one-liner not in Phobos, but in the documentation as an example of how to use it. The user will have to look up the function in the documentation anyway.
    Bottom line: one-liner-ness shouldn't be an automatic disqualifier.
As always, use good judgement. You and I differ on where that line is, however. I prefer a small interface with a minimal number of orthogonal functions, from which I can assemble what I need. An interface with a blizzard of functions all doing overlapping things with unknown tradeoffs is cognitive overload. The documentation of such an interface should, of course, provide examples of how to assemble those minimal functions into commonly needed solutions. --- As an aside, Andrei has worked very hard trying to figure out how to break down the memory allocator into the smallest collection of orthogonal parts that can then be assembled *by the user* into whatever he needs. It is not obvious, and amazingly nobody has done it before. https://dlang.org/phobos/std_experimental_allocator.html He did the same thing with the checked int package, which blows away every other checked integer solution I've seen. I believe it's the future of How To Do Interfaces Right :-) https://dlang.org/phobos/std_experimental_checkedint.html The documentation for both is a bit intimidating, though. There should be a more tutorial oriented overview.
Nov 22
parent reply sarn <sarn theartofmachinery.com> writes:
On Wednesday, 22 November 2017 at 22:17:05 UTC, Walter Bright 
wrote:
 On 11/22/2017 5:45 AM, Steven Schveighoffer wrote:
 1. All OS calls with timing requirements use non-floating 
 point to represent how long to sleep. After all a CPU uses 
 discrete math, and the timing implementation is no different.
Microsoft has numerous COM APIs for time functions. One of them I had to deal with used doubles to represent seconds. This had to be interfaced with other time functions that used integers. The trouble came from round trips - there were cases where double=>integer=>double did not produce the same result. Even worse, double=>integer=>double=>integer=>double ... produced a creeping shift in the value! It took much time to come up with a method of preventing such drift, and even that was unreliable.
It's fixed point, not floating point, but this famous software disaster is a pretty dramatic example: http://www-users.math.umn.edu/~arnold/disasters/patriot.html +1 to using integer arithmetic for the low-level time APIs.
Nov 22
parent captaindet <2krnk gmx.net> writes:
On 2017-11-23 12:55, sarn wrote:
 +1 to using integer arithmetic for the low-level time APIs.
and nobody is advocating to change this. it is about being able to use such result (duration) with non-time focused libraries/functions (eg. general maths/stats) expecting doubles. if this is not possible in an obvious/standard way (being to!... in D) this is the very moment all potential python-to-D converts will run away screaming. putting the solution for this common problem only in the docs will not appease them either but rather make them mock D. i still don't understand how problems could arise from a supported one-way conversion -> double. of course, double -> time or duration is something different and i tend to agree that this should be left for the user to implement. i for my part could live with just one supported to!double conversion resulting in seconds. (i guess i am biased as i prefer to use SI units only for all my calculations.) /det
Nov 22