Skip to content

Commit b42f16c

Browse files
author
HaskellMouse
committed
Fixed conversion to text builder.
Also fixed comments.
1 parent 8259f56 commit b42f16c

File tree

8 files changed

+45
-32
lines changed

8 files changed

+45
-32
lines changed

Data/Double/Conversion/ByteString.hs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,9 @@
1111
-- in new projects.
1212
-- Please, use Convertable type class from Data.Double.Conversion.Convertable
1313
--
14+
-- It is espesially recommended to convert a large amount of numbers via bytestring builder
15+
-- using methods of Convertable type class. It is about 10-15x faster.
16+
--
1417
-- Fast, efficient support for converting between double precision
1518
-- floating point values and text.
1619
--

Data/Double/Conversion/Convertable.hs

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -51,11 +51,15 @@ import qualified Data.Text.Internal.Builder as T (Builder)
5151
-- Compute the shortest string of digits that correctly represent
5252
-- the input number.
5353
--
54-
-- Conversion to text is twice faster than conversion to bytestring
5554
-- Conversion to text via Builder (both in the in case of bytestring and text) in case of single number
56-
-- is much slower, than to text or bytestring directly. (2-3x)
57-
-- But conversion large amount of numbers to text via Builder is much faster than directly (up to 50x).
58-
-- Conversion to text via text builder is a little slower, then via bytestring builder
55+
-- is a bit slower, than to text or bytestring directly.
56+
-- But conversion a large amount of numbers to text via Builder (for example using foldr) is much faster than direct conversion to Text (up to 10-15x).
57+
--
58+
-- The same works for bytestrings: conversion, for example, a list of 20000 doubles to bytestring builder
59+
-- and then to bytestring is about 13 times faster than direct conversion of this list to bytestring.
60+
--
61+
-- Conversion to text via text builder is a little bit slower, than conversion to bytestring via bytestring builder.
62+
5963

6064
class (RealFloat a, IsString b) => Convertable a b where
6165
toExponential :: Int -> a -> b

Data/Double/Conversion/Internal/ByteStringBuilder.hs

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,6 @@
1212
-- Fast, efficient support for converting between double precision
1313
-- floating point values and bytestring builder.
1414

15-
-- This functions are much slower on the single value, but also it is much faster in converting big set of
16-
-- numbers, than bytestring functions. See benchmark.
17-
1815
module Data.Double.Conversion.Internal.ByteStringBuilder
1916
(convert
2017
) where

Data/Double/Conversion/Internal/TextBuilder.hs

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -28,21 +28,27 @@ import qualified Data.Text.Array as A
2828
import Data.Text.Internal.Builder (Builder, writeN)
2929
import Foreign.C.Types (CDouble, CFloat, CInt)
3030
import GHC.Prim (MutableByteArray#)
31+
import Control.Monad.ST (runST)
3132

32-
-- | Not implemented yet
3333
convert :: (RealFloat a, RealFloat b, b ~ ForeignFloating a) => String -> CInt
3434
-> (forall s. b -> MutableByteArray# s -> IO CInt)
3535
-> a -> Builder
3636
{-# SPECIALIZE convert :: String -> CInt -> (forall s. CDouble -> MutableByteArray# s -> IO CInt) -> Double -> Builder #-}
3737
{-# SPECIALIZE convert :: String -> CInt -> (forall s. CFloat -> MutableByteArray# s -> IO CInt) -> Float -> Builder #-}
3838
{-# INLINABLE convert #-}
39+
convert func len act val = runST $ do
3940
#if MIN_VERSION_text(2,0,0)
40-
convert func len act val = writeN (fromIntegral len) $ \(A.MutableByteArray maBa) _ -> do
41+
mTempArr@(A.MutableByteArray tempMArr) <- A.new (fromIntegral len)
4142
#else
42-
convert func len act val = writeN (fromIntegral len) $ \(A.MArray maBa) _ -> do
43+
mTempArr@(A.MArray tempMArr) <- A.new (fromIntegral len)
44+
#endif
45+
size <- unsafeIOToST $ act (realToFrac val) tempMArr
46+
tempArr <- A.unsafeFreeze mTempArr
47+
when (size == -1) .
48+
fail $ "Data.Double.Conversion.Text." ++ func ++
49+
": conversion failed."
50+
#if MIN_VERSION_text(2,0,0)
51+
return $ writeN (fromIntegral size) $ \mArr _ -> A.copyI (fromIntegral size) mArr 0 tempArr 0
52+
#else
53+
return $ writeN (fromIntegral size) $ \mArr _ -> A.copyI mArr 0 tempArr 0 (fromIntegral size)
4354
#endif
44-
size <- unsafeIOToST $ act (realToFrac val) maBa
45-
when (size == -1) .
46-
error $ "Data.Double.Conversion.Text." ++ func ++
47-
": conversion failed (invalid precision requested)"
48-
return ()

Data/Double/Conversion/Text.hs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,9 @@
1313
-- in new projects.
1414
-- Please, use Convertable type class from Data.Double.Conversion.Convertable
1515
--
16+
-- It is espesially recommended to convert a large amount of numbers via text builder
17+
-- using methods of Convertable type class. It is about 10-15x faster.
18+
--
1619
-- Fast, efficient support for converting between double precision
1720
-- floating point values and text.
1821
--

benchmarks/Benchmarks.hs

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -18,34 +18,34 @@ testList = [2.08345919 .. 20002.08345919] :: [Double]
1818
testFloatList = [2.08345919 .. 20002.08345919] :: [Float]
1919

2020
testListBSBuilder :: (Double -> BB.Builder) -> [Double] -> BSS.ByteString
21-
testListBSBuilder func list = BSL.toStrict $ BBE.toLazyByteStringWith (BBE.safeStrategy 128 128) BSL.empty $ foldr (\x y -> (func x) <> y) mempty list
21+
testListBSBuilder func list = BSL.toStrict $ BBE.toLazyByteStringWith (BBE.safeStrategy 128 128) BSL.empty $ foldr (\x y -> (func x <> ", ") <> y) mempty list
2222

2323
testListBSBuilderFloat :: (Float -> BB.Builder) -> [Float] -> BSS.ByteString
24-
testListBSBuilderFloat func list = BSL.toStrict $ BBE.toLazyByteStringWith (BBE.safeStrategy 128 128) BSL.empty $ foldr (\x y -> (func x) <> y) mempty list
24+
testListBSBuilderFloat func list = BSL.toStrict $ BBE.toLazyByteStringWith (BBE.safeStrategy 128 128) BSL.empty $ foldr (\x y -> (func x <> ", ") <> y) mempty list
2525

2626
testListByteString :: (Double -> BSS.ByteString) -> [Double] -> BSS.ByteString
27-
testListByteString func = foldr (\x y -> (func x) <> y) mempty
27+
testListByteString func = foldr (\x y -> (func x <> ", ") <> y) mempty
2828

2929
testListByteStringFloat :: (Float -> BSS.ByteString) -> [Float] -> BSS.ByteString
30-
testListByteStringFloat func = foldr (\x y -> (func x) <> y) mempty
30+
testListByteStringFloat func = foldr (\x y -> (func x <> ", ") <> y) mempty
3131

3232
testListText :: (Double -> T.Text) -> [Double] -> T.Text
33-
testListText func = foldr (\x y -> (func x) <> y) mempty
33+
testListText func = foldr (\x y -> (func x <> ", ") <> y) mempty
3434

3535
testListTextFloat :: (Float -> T.Text) -> [Float] -> T.Text
36-
testListTextFloat func = foldr (\x y -> (func x) <> y) mempty
36+
testListTextFloat func = foldr (\x y -> (func x <> ", ") <> y) mempty
3737

3838
testListTextBuilder :: (Double -> BT.Builder) -> [Double] -> T.Text
39-
testListTextBuilder func list = TL.toStrict $ BT.toLazyText $ foldr (\x y -> (func x) <> y) mempty list
39+
testListTextBuilder func list = TL.toStrict $ BT.toLazyText $ foldr (\x y -> (func x <> ", ") <> y) mempty list
4040

4141
testListTextBuilderFloat :: (Float -> BT.Builder) -> [Float] -> T.Text
42-
testListTextBuilderFloat func list = TL.toStrict $ BT.toLazyText $ foldr (\x y -> (func x) <> y) mempty list
42+
testListTextBuilderFloat func list = TL.toStrict $ BT.toLazyText $ foldr (\x y -> (func x <> ", ") <> y) mempty list
4343

4444
testFuncBuilderDefault :: [Double] -> BSS.ByteString
45-
testFuncBuilderDefault = \list -> BSL.toStrict $ BB.toLazyByteString $ foldr (\x y -> (BB.doubleDec x) <> y) mempty list
45+
testFuncBuilderDefault = \list -> BSL.toStrict $ BB.toLazyByteString $ foldr (\x y -> (BB.doubleDec x <> ", ") <> y) mempty list
4646

4747
testFuncBuilderDefaultFloat :: [Float] -> BSS.ByteString
48-
testFuncBuilderDefaultFloat = \list -> BSL.toStrict $ BB.toLazyByteString $ foldr (\x y -> (BB.floatDec x) <> y) mempty list
48+
testFuncBuilderDefaultFloat = \list -> BSL.toStrict $ BB.toLazyByteString $ foldr (\x y -> (BB.floatDec x <> ", ") <> y) mempty list
4949

5050

5151
main = defaultMain [

benchmarks/cabal.project

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
packages: . ../double-conversion.cabal

double-conversion.cabal

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@ name: double-conversion
22
version: 2.0.4.1
33
license: BSD3
44
license-file: LICENSE
5-
homepage: https://github.com/Haskell-mouse/double-conversion
6-
bug-reports: https://github.com/Haskell-mouse/double-conversion/issues
5+
homepage: https://github.com/haskell/double-conversion
6+
bug-reports: https://github.com/haskell/double-conversion/issues
77
category: Text
88
author: Bryan O'Sullivan <bos@serpentine.com>
99
maintainer: Bryan O'Sullivan <bos@serpentine.com>
@@ -25,12 +25,11 @@ description:
2525
The 'Text' versions of these functions are about 30 times faster
2626
than the default 'show' implementation for the 'Double' type.
2727
.
28-
The 'ByteString' versions are /slower/ than the 'Text' versions;
29-
roughly half the speed. (This seems to be due to the cost of
30-
allocating 'ByteString' values via @malloc@.)
28+
The 'ByteString' versions are have very close speed to the 'Text' versions;
3129
.
32-
Builder versions are slower on single value, but they are much faster on large number of values
33-
(up to 50x faster on list with 20000 doubles).
30+
Builder versions (both for Text and Bytestring) are slower on single value,
31+
but they are much faster on large number of values
32+
(up to 20x faster on list with 20000 doubles).
3433
.
3534
As a final note, be aware that the @bytestring-show@ package is
3635
about 50% slower than simply using 'show'.

0 commit comments

Comments
 (0)