Skip to content

Commit 89f2908

Browse files
Update docs/refactor for time modules
1 parent 7d2863b commit 89f2908

File tree

3 files changed

+126
-30
lines changed

3 files changed

+126
-30
lines changed

core/src/Streamly/Internal/Data/Time/TimeSpec.hsc

Lines changed: 33 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,33 @@
11
{-# OPTIONS_GHC -Wno-identities #-}
22

3-
#ifndef __GHCJS__
4-
#include "config.h"
5-
#endif
6-
7-
#include "Streamly/Internal/Data/Time/Clock/config-clock.h"
8-
9-
#include "MachDeps.h"
10-
113
-- |
124
-- Module : Streamly.Internal.Data.Time.TimeSpec
135
-- Copyright : (c) 2019 Composewell Technologies
146
-- License : BSD3
157
-- Maintainer : [email protected]
168
-- Stability : experimental
179
-- Portability : GHC
10+
--
11+
-- 'TimeSpec' can store upto a duration of ~292 billion years at nanosecond
12+
-- precision. An 'Int64' data type is much faster to manipulate but has a
13+
-- smaller maximum limit (~292 years) at nanosecond precision. An 'Integer'
14+
-- type can possibly be used for unbounded fixed precision time. 'Double' can
15+
-- be used for floating precision time.
1816

1917
module Streamly.Internal.Data.Time.TimeSpec
2018
(
2119
TimeSpec(..)
2220
)
2321
where
2422

23+
#ifndef __GHCJS__
24+
#include "config.h"
25+
#endif
26+
27+
#include "Streamly/Internal/Data/Time/config-clock.h"
28+
29+
#include "MachDeps.h"
30+
2531
import Data.Int (Int64)
2632
#if (WORD_SIZE_IN_BITS == 32)
2733
import Data.Int (Int32)
@@ -41,20 +47,27 @@ tenPower9 :: Int64
4147
tenPower9 = 1000000000
4248

4349
-------------------------------------------------------------------------------
44-
-- TimeSpec representation
50+
-- TimeSpec
4551
-------------------------------------------------------------------------------
4652

47-
-- A structure storing seconds and nanoseconds as 'Int64' is the simplest and
48-
-- fastest way to store practically large quantities of time with efficient
49-
-- arithmetic operations. If we store nanoseconds using 'Integer' it can store
50-
-- practically unbounded quantities but it may not be as efficient to
51-
-- manipulate in performance critical applications. XXX need to measure the
52-
-- performance.
53+
-- XXX Should we use "SystemTime" from the "time" package instead?
54+
--
55+
-- | 'TimeSpec' can hold up to ~292 billion years at nanosecond precision.
56+
--
57+
-- In addition to using the 'TimeSpec' constructor you can also use
58+
-- 'fromInteger' from the 'Num' type class to create a 'TimeSpec' from
59+
-- nanoseconds. Like any 'Num', 'TimeSpec' can be negative.
60+
--
61+
-- Note, we assume that 'nsec' is always less than 10^9. Also, when 'TimeSpec'
62+
-- is negative then both 'sec' and 'nsec' must be negative.
63+
-- TODO: Use smart constructors to enforce the assumptions.
64+
--
65+
-- Use 'Eq' and 'Ord' instances for comparisons and the 'Num' instance to
66+
-- perform arithmetic operations on 'TimeSpec'.
5367
--
54-
-- | Data type to represent practically large quantities of time efficiently.
55-
-- It can represent time up to ~292 billion years at nanosecond resolution.
5668
data TimeSpec = TimeSpec
5769
{ sec :: {-# UNPACK #-} !Int64 -- ^ seconds
70+
-- XXX this could be Int32 instead
5871
, nsec :: {-# UNPACK #-} !Int64 -- ^ nanoseconds
5972
} deriving (Eq, Read, Show)
6073

@@ -88,11 +101,12 @@ adjustSign t@(TimeSpec s ns)
88101
timeSpecToInteger :: TimeSpec -> Integer
89102
timeSpecToInteger (TimeSpec s ns) = toInteger $ s * tenPower9 + ns
90103

104+
-- | Note that the arithmetic operations may overflow silently.
91105
instance Num TimeSpec where
92106
{-# INLINE (+) #-}
93107
t1 + t2 = adjustSign (addWithOverflow t1 t2)
94108

95-
-- XXX will this be more optimal if imlemented without "negate"?
109+
-- XXX will this be more optimal if implemented without "negate"?
96110
{-# INLINE (-) #-}
97111
t1 - t2 = t1 + negate t2
98112
t1 * t2 = fromInteger $ timeSpecToInteger t1 * timeSpecToInteger t2
@@ -104,7 +118,7 @@ instance Num TimeSpec where
104118
{-# INLINE signum #-}
105119
signum (TimeSpec s ns) | s == 0 = TimeSpec (signum ns) 0
106120
| otherwise = TimeSpec (signum s) 0
107-
-- This is fromNanoSecond64 Integer
121+
-- | Convert 'Integer' nanoseconds to 'TimeSpec'.
108122
{-# INLINE fromInteger #-}
109123
fromInteger nanosec = TimeSpec (fromInteger s) (fromInteger ns)
110124
where (s, ns) = nanosec `divMod` toInteger tenPower9

core/src/Streamly/Internal/Data/Time/Units.hs

Lines changed: 88 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,80 @@
11
-- |
22
-- Module : Streamly.Internal.Data.Time.Units
33
-- Copyright : (c) 2019 Composewell Technologies
4-
--
5-
-- License : BSD3
4+
-- License : BSD-3-Clause
65
-- Maintainer : [email protected]
76
-- Stability : pre-release
87
-- Portability : GHC
8+
--
9+
-- Fast time manipulation.
10+
--
11+
-- = Representing Time
12+
--
13+
-- Numbers along with an associated unit (e.g. 'MilliSecond64') are used to
14+
-- represent durations and points in time. Durations are relative but points
15+
-- are absolute and defined with respect to some fixed or well known point in
16+
-- time e.g. the Unix epoch (01-Jan-1970). Absolute and relative times are
17+
-- numbers that can be represented and manipulated like 'Num'.
18+
--
19+
-- = Fixed Precision 64-bit Units
20+
--
21+
-- * 'NanoSecond64': 292 years at nanosecond precision.
22+
-- * 'MicroSecond64': 292K years at nanosecond precision.
23+
-- * 'MilliSecond64': 292M years at nanosecond precision.
24+
--
25+
-- These units are 'Integral' 'Num' types. We can use 'fromIntegral' to convert
26+
-- any integral type to/from these types.
27+
--
28+
-- = TimeSpec
29+
--
30+
-- * 'TimeSpec': 292G years at nanosecond precision
31+
--
32+
-- = RelTime64
33+
--
34+
-- Relative time, not relative to any specific epoch. Represented using
35+
-- 'NanoSecond64'. 'fromRelTime64' and 'toRelTime64' can be used to convert a
36+
-- time unit to/from RelTime. Note that a larger unit e.g. 'MicroSecond64' may
37+
-- get truncated if it is larger than 292 years. RelTime64 is also generated by
38+
-- diffing two AbsTime.
39+
--
40+
-- RelTime is a 'Num', we can do number arithmetic on RelTime, and use
41+
-- 'fromInteger' to convert an 'Integer' nanoseconds to 'RelTime'.
42+
--
43+
-- = AbsTime
44+
--
45+
-- Time measured relative to the POSIX epoch i.e. 01-Jan-1970. Represented
46+
-- using 'TimeSpec'. 'fromAbsTime' and 'toAbsTime' can be used to convert a
47+
-- time unit to/from AbsTime.
48+
--
49+
-- AbsTime is not a 'Num'. We can use 'diffAbsTime' to diff abstimes to get
50+
-- a 'RelTime'. We can add RelTime to AbsTime to get another AbsTime.
51+
--
52+
-- = TimeSpec vs 64-bit Units
53+
--
54+
-- TimeSpec can represent up to 292 billion years of time at nanosecond
55+
-- precision while 64-bit units can represent only 292 years at the same
56+
-- precision. However, 64-bit units are much faster to manipulate. In high
57+
-- performance code it is recommended to use the 64-bit units if possible.
58+
--
59+
-- = Working with the "time" package
60+
--
61+
-- AbsTime is essentially the same as 'SystemTime' from the time package. We
62+
-- can use 'SystemTime' to interconvert between time package and this module.
63+
--
64+
-- = Alternative Representations
65+
--
66+
-- Double or Fixed would be a much better representation so that we do not lose
67+
-- information between conversions. However, for faster arithmetic operations
68+
-- we use an 'Int64' here. When we need convservation of values we can use a
69+
-- different system of units with a Fixed precision.
70+
--
71+
-- = TODO
72+
--
73+
-- Split the Timespec/TimeUnit in a separate module?
74+
-- Keep *64/TimeUnit64 in this module, remove the 64 suffix because these are
75+
-- common units.
76+
-- Rename TimeUnit to IsTimeSpec, TimeUnit64 to IsTimeUnit.
77+
--
978

1079
module Streamly.Internal.Data.Time.Units
1180
(
@@ -83,11 +152,6 @@ tenPower9 = 1000000000
83152
-- NanoSecond Int64
84153
-- ...
85154

86-
-- Double or Fixed would be a much better representation so that we do not lose
87-
-- information between conversions. However, for faster arithmetic operations
88-
-- we use an 'Int64' here. When we need convservation of values we can use a
89-
-- different system of units with a Fixed precision.
90-
91155
-------------------------------------------------------------------------------
92156
-- Integral Units
93157
-------------------------------------------------------------------------------
@@ -152,19 +216,21 @@ newtype MilliSecond64 = MilliSecond64 Int64
152216
-- performance boost. If not then we can just use Integer nanoseconds and get
153217
-- rid of TimeUnitWide.
154218
--
219+
{-
155220
-- | A type class for converting between time units using 'Integer' as the
156221
-- intermediate and the widest representation with a nanosecond resolution.
157222
-- This system of units can represent arbitrarily large times but provides
158223
-- least efficient arithmetic operations due to 'Integer' arithmetic.
159224
--
160225
-- NOTE: Converting to and from units may truncate the value depending on the
161226
-- original value and the size and resolution of the destination unit.
162-
{-
163227
class TimeUnitWide a where
164228
toTimeInteger :: a -> Integer
165229
fromTimeInteger :: Integer -> a
166230
-}
167231

232+
-- XXX Rename to IsTimeUnit?
233+
--
168234
-- | A type class for converting between units of time using 'TimeSpec' as the
169235
-- intermediate representation. This system of units can represent up to ~292
170236
-- billion years at nanosecond resolution with reasonably efficient arithmetic
@@ -254,18 +320,27 @@ instance TimeUnit64 MilliSecond64 where
254320
-- Absolute time
255321
-------------------------------------------------------------------------------
256322

323+
-- Have a Fixed64 type with an Int64 as underlying type
324+
-- XXX Use AbsTime64 for faster arithmetic on AbsTimes?
325+
--
326+
-- data Epoch = Posix | UTC | Rel
327+
--
328+
-- XXX data Time epoch = Time TimeSpec
329+
--
257330
-- | Absolute times are relative to a predefined epoch in time. 'AbsTime'
258331
-- represents times using 'TimeSpec' which can represent times up to ~292
259332
-- billion years at a nanosecond resolution.
260333
newtype AbsTime = AbsTime TimeSpec
261334
deriving (Eq, Ord, Show)
262335

263-
-- | Convert a 'TimeUnit' to an absolute time.
336+
-- | Convert a 'TimeUnit' representing relative time from the Unix epoch to an
337+
-- absolute time.
264338
{-# INLINE_NORMAL toAbsTime #-}
265339
toAbsTime :: TimeUnit a => a -> AbsTime
266340
toAbsTime = AbsTime . toTimeSpec
267341

268-
-- | Convert absolute time to a 'TimeUnit'.
342+
-- | Convert absolute time to a relative 'TimeUnit' representing time relative
343+
-- to the Unix epoch.
269344
{-# INLINE_NORMAL fromAbsTime #-}
270345
fromAbsTime :: TimeUnit a => AbsTime -> a
271346
fromAbsTime (AbsTime t) = fromTimeSpec t
@@ -286,8 +361,10 @@ fromAbsTime (AbsTime t) = fromTimeSpec t
286361
-- usually shorter and for our purposes an Int64 nanoseconds can hold close to
287362
-- thousand year duration. It is also faster to manipulate. We do not check for
288363
-- overflows during manipulations so use it only when you know the time cannot
289-
-- be too big. If you need a bigger RelTime representation then use RelTimeBig.
364+
-- be too big. If you need a bigger RelTime representation then use RelTime.
290365

366+
-- This is the same as the DiffTime in time package.
367+
--
291368
-- | Relative times are relative to some arbitrary point of time. Unlike
292369
-- 'AbsTime' they are not relative to a predefined epoch.
293370
newtype RelTime64 = RelTime64 NanoSecond64

src/Streamly/Internal/Data/Stream/IsStream/Transform.hs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1339,6 +1339,11 @@ indexedR n = fromStreamD . D.indexedR n . toStreamD
13391339
-- Time Indexing
13401340
-------------------------------------------------------------------------------
13411341

1342+
-- XXX Use the timestamp as (AbsTime, RelTime, a). AbsTime is the time when we
1343+
-- started evaluating the stream and RelTime is the time relative to that.
1344+
-- When we use only RelTime, AbsTime would be discarded by the compiler so it
1345+
-- should not pose any overhead. Have some benchmarks to prove that.
1346+
13421347
-- Note: The timestamp stream must be the second stream in the zip so that the
13431348
-- timestamp is generated after generating the stream element and not before.
13441349
-- If we do not do that then the following example will generate the same

0 commit comments

Comments
 (0)