on master help sign in
Make UTCTime encoding 3x faster

It's now over 10x faster than the formatTime-based approach. In this instance, the win comes from fixed-width arithmetic (Int64 vs Integer).

commit27f250ba0a4a9bc93879ce06610434c2ceab64a6 authorBryan O'Sullivan <bos@serpentine.com> dateMon 20 Jul 2015, 21:32 (11 years ago) parent298f0a0
2 files changed+24−11
Data/Aeson/Encode/Builder.hs+23−10 Data/Aeson/Types/Instances.hs+1−1
Data/Aeson/Encode/Builder.hs+23−10
@@ -25,8 +25,6 @@ module Data.Aeson.Encode.Builder
2525 , unquoted
2626 , number
2727 , day
28− , timeOfDay
29− , timeZone
3028 , utcTime
3129 , zonedTime
3230 , ascii2
@@ -41,12 +39,14 @@ import Data.ByteString.Builder.Prim as BP
4139import Data.ByteString.Builder.Scientific (scientificBuilder)
4240import Data.Char (chr, ord)
4341import Data.Foldable (foldMap)
42+import Data.Int (Int64)
4443import Data.Monoid ((<>))
4544import Data.Scientific (Scientific, base10Exponent, coefficient)
46−import Data.Time (UTCTime(..))
45+import Data.Time (DiffTime, UTCTime(..))
4746import Data.Time.Calendar (Day(..), toGregorian)
4847import Data.Time.LocalTime
4948import Data.Word (Word8)
49+import Unsafe.Coerce (unsafeCoerce)
5050import qualified Data.HashMap.Strict as HMS
5151import qualified Data.Text as T
5252import qualified Data.Text.Encoding as TE
@@ -177,8 +177,8 @@ day dd = B.integerDec y <>
177177 !(T dh dl) = twoDigits d
178178{-# INLINE day #-}
179179
180−timeOfDay :: TimeOfDay -> Builder
181−timeOfDay (TimeOfDay h m s)
180+timeOfDay :: TimeOfDay64 -> Builder
181+timeOfDay (TOD h m s)
182182 | micro100 < 5 = hhmmss -- omit trailing milliseconds if zero
183183 | otherwise = hhmmss <> BP.primBounded (ascii4 ('.',(a,(b,c)))) ()
184184 where
@@ -186,8 +186,7 @@ timeOfDay (TimeOfDay h m s)
186186 !(T hh hl) = twoDigits h
187187 !(T mh ml) = twoDigits m
188188 !(T sh sl) = twoDigits (fromIntegral real)
189− picos = fromPico s
190− (real,frac) = picos `quotRem` 1000000000000
189+ (real,frac) = s `quotRem` 1000000000000
191190 -- Units of 100 microseconds (tenths of a millisecond), which we
192191 -- round up so that milliseconds >= 0.5 render correctly.
193192 micro100
@@ -209,16 +208,30 @@ timeZone (TimeZone off _ _)
209208 (h,m) = abs off `quotRem` 60
210209{-# INLINE timeZone #-}
211210
212−dayTime :: Day -> TimeOfDay -> Builder
211+dayTime :: Day -> TimeOfDay64 -> Builder
213212dayTime d t = day d <> B.char7 'T' <> timeOfDay t
214213{-# INLINE dayTime #-}
215214
216215utcTime :: UTCTime -> B.Builder
217−utcTime (UTCTime d s) = dayTime d (timeToTimeOfDay s) <> B.char7 'Z'
216+utcTime (UTCTime d s) = dayTime d (diffTimeOfDay64 s) <> B.char7 'Z'
218217{-# INLINE utcTime #-}
219218
219+data TimeOfDay64 = TOD {-# UNPACK #-} !Int
220+ {-# UNPACK #-} !Int
221+ {-# UNPACK #-} !Int64
222+
223+diffTimeOfDay64 :: DiffTime -> TimeOfDay64
224+diffTimeOfDay64 t = TOD (fromIntegral h) (fromIntegral m) s
225+ where (h,mp) = fromIntegral pico `quotRem` 3600000000000000
226+ (m,s) = mp `quotRem` 60000000000000
227+ pico = unsafeCoerce t :: Integer
228+
229+toTimeOfDay64 :: TimeOfDay -> TimeOfDay64
230+toTimeOfDay64 (TimeOfDay h m s) = TOD h m (fromIntegral (fromPico s))
231+
220232zonedTime :: ZonedTime -> Builder
221−zonedTime (ZonedTime (LocalTime d t) z) = dayTime d t <> timeZone z
233+zonedTime (ZonedTime (LocalTime d t) z) =
234+ dayTime d (toTimeOfDay64 t) <> timeZone z
222235{-# INLINE zonedTime #-}
223236
224237data T = T {-# UNPACK #-} !Char {-# UNPACK #-} !Char
Data/Aeson/Types/Instances.hs+1−1
@@ -77,7 +77,7 @@ import Data.Maybe (fromMaybe)
7777import Data.Monoid (Dual(..), First(..), Last(..))
7878import Data.Ratio (Ratio, (%), numerator, denominator)
7979import Data.Text (Text, pack, unpack)
80−import Data.Time (UTCTime(..), ZonedTime(..), TimeZone(..))
80+import Data.Time (UTCTime, ZonedTime)
8181import Data.Time.Format (FormatTime, formatTime, parseTime)
8282import Data.Traversable as Tr (sequence, traverse)
8383import Data.Vector (Vector)