Skip to content

Commit 0653030

Browse files
Fast way on 64-bit, slow way on 32-bit.
1 parent 316a206 commit 0653030

File tree

8 files changed

+148
-110
lines changed

8 files changed

+148
-110
lines changed

Data/Atomic.hs

Lines changed: 55 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -24,54 +24,79 @@ import GHC.IO
2424
import GHC.Prim
2525

2626
#include "MachDeps.h"
27-
28-
#ifndef WORD_SIZE_IN_BITS
29-
#error "WORD_SIZE_IN_BITS not defined"
30-
#elif WORD_SIZE_IN_BITS == 32
31-
#define ARRLEN 4
32-
#elif WORD_SIZE_IN_BITS == 64
33-
#define ARRLEN 8
34-
#else
35-
#error "WORD_SIZE_IN_BITS not 32 or 64"
27+
#ifndef SIZEOF_HSINT
28+
#error "MachDeps.h didn't define SIZEOF_HSINT"
3629
#endif
3730

31+
-- 64-bit machine, Int ~ Int64, do it the fast way:
32+
#if SIZEOF_HSINT == 8
33+
3834
-- | A mutable, atomic integer.
3935
data Atomic = C (MutableByteArray# RealWorld)
4036

4137
-- | Create a new, zero initialized, atomic.
42-
new :: Int -> IO Atomic
43-
new (I# n) = IO $ \s ->
44-
case newByteArray# ARRLEN# s of { (# s1, mba #) ->
45-
case atomicWriteIntArray# mba 0# n s1 of { s2 ->
38+
new :: Int64 -> IO Atomic
39+
new (I64# n64) = IO $ \s ->
40+
case newByteArray# SIZEOF_HSINT# s of { (# s1, mba #) ->
41+
case atomicWriteIntArray# mba 0# (int64ToInt# n64) s1 of { s2 ->
4642
(# s2, C mba #) }}
4743

48-
read :: Atomic -> IO Int
44+
read :: Atomic -> IO Int64
4945
read (C mba) = IO $ \s ->
5046
case atomicReadIntArray# mba 0# s of { (# s1, n #) ->
51-
(# s1, I# n #)}
47+
(# s1, I64# (intToInt64# n) #)}
5248

5349
-- | Set the atomic to the given value.
54-
write :: Atomic -> Int -> IO ()
55-
write (C mba) (I# n) = IO $ \s ->
56-
case atomicWriteIntArray# mba 0# n s of { s1 ->
50+
write :: Atomic -> Int64 -> IO ()
51+
write (C mba) (I64# n64) = IO $ \s ->
52+
case atomicWriteIntArray# mba 0# (int64ToInt# n64) s of { s1 ->
53+
(# s1, () #) }
54+
55+
-- | Increase the atomic by the given amount.
56+
add :: Atomic -> Int64 -> IO ()
57+
add (C mba) (I64# n64) = IO $ \s ->
58+
case fetchAddIntArray# mba 0# (int64ToInt# n64) s of { (# s1, _ #) ->
5759
(# s1, () #) }
5860

61+
-- | Decrease the atomic by the given amount.
62+
subtract :: Atomic -> Int64 -> IO ()
63+
subtract (C mba) (I64# n64) = IO $ \s ->
64+
case fetchSubIntArray# mba 0# (int64ToInt# n64) s of { (# s1, _ #) ->
65+
(# s1, () #) }
66+
67+
#else
68+
69+
-- 32-bit machine, Int ~ Int32, fall back to IORef. This could be replaced with
70+
-- faster implementations for specific 32-bit machines in the future, but the
71+
-- idea is to preserve 64-bit width for counters.
72+
73+
newtype Atomic = C (IORef Int64)
74+
75+
-- | Create a new, zero initialized, atomic.
76+
new :: Int64 -> IO Atomic
77+
new = fmap C . newIORef
78+
79+
read :: Atomic -> IO Int64
80+
read (C ior) = readIORef ior
81+
82+
-- | Set the atomic to the given value.
83+
write :: Atomic -> Int64 -> IO ()
84+
write (C ior) !i = atomicWriteIORef ior i
85+
86+
-- | Increase the atomic by the given amount.
87+
add :: Atomic -> Int64 -> IO ()
88+
add (C ior) !i = atomicModifyIORef' ior (\!n -> (n+i, ()))
89+
90+
-- | Decrease the atomic by the given amount.
91+
subtract :: Atomic -> Int64 -> IO ()
92+
subtract (C ior) !i = atomicModifyIORef' ior (\!n -> (n-i, ()))
93+
94+
#endif
95+
5996
-- | Increase the atomic by one.
6097
inc :: Atomic -> IO ()
6198
inc atomic = add atomic 1
6299

63100
-- | Decrease the atomic by one.
64101
dec :: Atomic -> IO ()
65102
dec atomic = subtract atomic 1
66-
67-
-- | Increase the atomic by the given amount.
68-
add :: Atomic -> Int -> IO ()
69-
add (C mba) (I# n) = IO $ \s ->
70-
case fetchAddIntArray# mba 0# n s of { (# s1, _ #) ->
71-
(# s1, () #) }
72-
73-
-- | Decrease the atomic by the given amount.
74-
subtract :: Atomic -> Int -> IO ()
75-
subtract (C mba) (I# n) = IO $ \s ->
76-
case fetchSubIntArray# mba 0# n s of { (# s1, _ #) ->
77-
(# s1, () #) }

System/Metrics.hs

Lines changed: 44 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ module System.Metrics
6969
) where
7070

7171
import Control.Monad (forM)
72+
import Data.Int (Int64)
7273
import qualified Data.IntMap.Strict as IM
7374
import Data.IORef (IORef, atomicModifyIORef, newIORef, readIORef)
7475
import qualified Data.HashMap.Strict as M
@@ -131,8 +132,8 @@ data GroupSampler = forall a. GroupSampler
131132
}
132133

133134
-- TODO: Rename this to Metric and Metric to SampledMetric.
134-
data MetricSampler = CounterS !(IO Int)
135-
| GaugeS !(IO Int)
135+
data MetricSampler = CounterS !(IO Int64)
136+
| GaugeS !(IO Int64)
136137
| LabelS !(IO T.Text)
137138
| DistributionS !(IO Distribution.Stats)
138139

@@ -154,18 +155,18 @@ newStore = do
154155
-- | Register a non-negative, monotonically increasing, integer-valued
155156
-- metric. The provided action to read the value must be thread-safe.
156157
-- Also see 'createCounter'.
157-
registerCounter :: T.Text -- ^ Counter name
158-
-> IO Int -- ^ Action to read the current metric value
159-
-> Store -- ^ Metric store
158+
registerCounter :: T.Text -- ^ Counter name
159+
-> IO Int64 -- ^ Action to read the current metric value
160+
-> Store -- ^ Metric store
160161
-> IO ()
161162
registerCounter name sample store =
162163
register name (CounterS sample) store
163164

164165
-- | Register an integer-valued metric. The provided action to read
165166
-- the value must be thread-safe. Also see 'createGauge'.
166-
registerGauge :: T.Text -- ^ Gauge name
167-
-> IO Int -- ^ Action to read the current metric value
168-
-> Store -- ^ Metric store
167+
registerGauge :: T.Text -- ^ Gauge name
168+
-> IO Int64 -- ^ Action to read the current metric value
169+
-> Store -- ^ Metric store
169170
-> IO ()
170171
registerGauge name sample store =
171172
register name (GaugeS sample) store
@@ -329,16 +330,6 @@ createDistribution name store = do
329330
-- easily be added to a metrics store by calling their register
330331
-- function.
331332

332-
#if MIN_VERSION_base(4,10,0)
333-
-- | Convert nanoseconds to milliseconds.
334-
nsToMs :: Int -> Int
335-
nsToMs s = round (realToFrac s / (1000000.0 :: Double))
336-
#else
337-
-- | Convert seconds to milliseconds.
338-
sToMs :: Double -> Int
339-
sToMs s = round (s * 1000.0)
340-
#endif
341-
342333
-- | Register a number of metrics related to garbage collector
343334
-- behavior.
344335
--
@@ -447,15 +438,15 @@ registerGcMetrics =
447438
, ("rts.gc.cumulative_bytes_used" , Counter . fromIntegral . Stats.cumulative_live_bytes)
448439
, ("rts.gc.bytes_copied" , Counter . fromIntegral . Stats.copied_bytes)
449440
#if MIN_VERSION_base(4,12,0)
450-
, ("rts.gc.init_cpu_ms" , Counter . nsToMs . fromIntegral . Stats.init_cpu_ns)
451-
, ("rts.gc.init_wall_ms" , Counter . nsToMs . fromIntegral . Stats.init_elapsed_ns)
441+
, ("rts.gc.init_cpu_ms" , Counter . nsToMs . Stats.init_cpu_ns)
442+
, ("rts.gc.init_wall_ms" , Counter . nsToMs . Stats.init_elapsed_ns)
452443
#endif
453-
, ("rts.gc.mutator_cpu_ms" , Counter . nsToMs . fromIntegral . Stats.mutator_cpu_ns)
454-
, ("rts.gc.mutator_wall_ms" , Counter . nsToMs . fromIntegral . Stats.mutator_elapsed_ns)
455-
, ("rts.gc.gc_cpu_ms" , Counter . nsToMs . fromIntegral . Stats.gc_cpu_ns)
456-
, ("rts.gc.gc_wall_ms" , Counter . nsToMs . fromIntegral . Stats.gc_elapsed_ns)
457-
, ("rts.gc.cpu_ms" , Counter . nsToMs . fromIntegral . Stats.cpu_ns)
458-
, ("rts.gc.wall_ms" , Counter . nsToMs . fromIntegral . Stats.elapsed_ns)
444+
, ("rts.gc.mutator_cpu_ms" , Counter . nsToMs . Stats.mutator_cpu_ns)
445+
, ("rts.gc.mutator_wall_ms" , Counter . nsToMs . Stats.mutator_elapsed_ns)
446+
, ("rts.gc.gc_cpu_ms" , Counter . nsToMs . Stats.gc_cpu_ns)
447+
, ("rts.gc.gc_wall_ms" , Counter . nsToMs . Stats.gc_elapsed_ns)
448+
, ("rts.gc.cpu_ms" , Counter . nsToMs . Stats.cpu_ns)
449+
, ("rts.gc.wall_ms" , Counter . nsToMs . Stats.elapsed_ns)
459450
, ("rts.gc.max_bytes_used" , Gauge . fromIntegral . Stats.max_live_bytes)
460451
, ("rts.gc.max_large_bytes_used" , Gauge . fromIntegral . Stats.max_large_objects_bytes)
461452
, ("rts.gc.max_compact_bytes_used" , Gauge . fromIntegral . Stats.max_compact_bytes)
@@ -469,42 +460,46 @@ registerGcMetrics =
469460
#if MIN_VERSION_base(4,11,0)
470461
, ("rts.gc.par_balanced_bytes_copied", Gauge . fromIntegral . Stats.cumulative_par_balanced_copied_bytes)
471462
#if MIN_VERSION_base(4,15,0)
472-
, ("rts.gc.nm.sync_cpu_ms" , Counter . nsToMs . fromIntegral . Stats.nonmoving_gc_sync_cpu_ns)
473-
, ("rts.gc.nm.sync_elapsed_ms" , Counter . nsToMs . fromIntegral . Stats.nonmoving_gc_sync_elapsed_ns)
474-
, ("rts.gc.nm.sync_max_elapsed_ms" , Counter . nsToMs . fromIntegral . Stats.nonmoving_gc_sync_max_elapsed_ns)
475-
, ("rts.gc.nm.cpu_ms" , Counter . nsToMs . fromIntegral . Stats.nonmoving_gc_cpu_ns)
476-
, ("rts.gc.nm.elapsed_ms" , Counter . nsToMs . fromIntegral . Stats.nonmoving_gc_elapsed_ns)
477-
, ("rts.gc.nm.max_elapsed_ms" , Counter . nsToMs . fromIntegral . Stats.nonmoving_gc_max_elapsed_ns)
463+
, ("rts.gc.nm.sync_cpu_ms" , Counter . nsToMs . Stats.nonmoving_gc_sync_cpu_ns)
464+
, ("rts.gc.nm.sync_elapsed_ms" , Counter . nsToMs . Stats.nonmoving_gc_sync_elapsed_ns)
465+
, ("rts.gc.nm.sync_max_elapsed_ms" , Counter . nsToMs . Stats.nonmoving_gc_sync_max_elapsed_ns)
466+
, ("rts.gc.nm.cpu_ms" , Counter . nsToMs . Stats.nonmoving_gc_cpu_ns)
467+
, ("rts.gc.nm.elapsed_ms" , Counter . nsToMs . Stats.nonmoving_gc_elapsed_ns)
468+
, ("rts.gc.nm.max_elapsed_ms" , Counter . nsToMs . Stats.nonmoving_gc_max_elapsed_ns)
478469
# endif
479470
# endif
480471
])
481472
getRTSStats
473+
where
474+
-- | Convert nanoseconds to milliseconds.
475+
nsToMs :: Int64 -> Int64
476+
nsToMs s = round (realToFrac s / (1000000.0 :: Double))
482477
#else
483478
(M.fromList
484-
[ ("rts.gc.bytes_allocated" , Counter . fromIntegral . Stats.bytesAllocated)
485-
, ("rts.gc.num_gcs" , Counter . fromIntegral . Stats.numGcs)
486-
, ("rts.gc.num_bytes_usage_samples" , Counter . fromIntegral . Stats.numByteUsageSamples)
487-
, ("rts.gc.cumulative_bytes_used" , Counter . fromIntegral . Stats.cumulativeBytesUsed)
488-
, ("rts.gc.bytes_copied" , Counter . fromIntegral . Stats.bytesCopied)
479+
[ ("rts.gc.bytes_allocated" , Counter . Stats.bytesAllocated)
480+
, ("rts.gc.num_gcs" , Counter . Stats.numGcs)
481+
, ("rts.gc.num_bytes_usage_samples" , Counter . Stats.numByteUsageSamples)
482+
, ("rts.gc.cumulative_bytes_used" , Counter . Stats.cumulativeBytesUsed)
483+
, ("rts.gc.bytes_copied" , Counter . Stats.bytesCopied)
489484
, ("rts.gc.mutator_cpu_ms" , Counter . sToMs . Stats.mutatorCpuSeconds)
490485
, ("rts.gc.mutator_wall_ms" , Counter . sToMs . Stats.mutatorWallSeconds)
491486
, ("rts.gc.gc_cpu_ms" , Counter . sToMs . Stats.gcCpuSeconds)
492487
, ("rts.gc.gc_wall_ms" , Counter . sToMs . Stats.gcWallSeconds)
493488
, ("rts.gc.cpu_ms" , Counter . sToMs . Stats.cpuSeconds)
494489
, ("rts.gc.wall_ms" , Counter . sToMs . Stats.wallSeconds)
495-
, ("rts.gc.max_bytes_used" , Gauge . fromIntegral . Stats.maxBytesUsed)
496-
, ("rts.gc.current_bytes_used" , Gauge . fromIntegral . Stats.currentBytesUsed)
497-
, ("rts.gc.current_bytes_slop" , Gauge . fromIntegral . Stats.currentBytesSlop)
498-
, ("rts.gc.max_bytes_slop" , Gauge . fromIntegral . Stats.maxBytesSlop)
499-
, ("rts.gc.peak_megabytes_allocated" , Gauge . fromIntegral . Stats.peakMegabytesAllocated)
500-
, ("rts.gc.par_tot_bytes_copied" , Gauge . fromIntegral . gcParTotBytesCopied)
501-
, ("rts.gc.par_avg_bytes_copied" , Gauge . fromIntegral . gcParTotBytesCopied)
502-
, ("rts.gc.par_max_bytes_copied" , Gauge . fromIntegral . Stats.parMaxBytesCopied)
490+
, ("rts.gc.max_bytes_used" , Gauge . Stats.maxBytesUsed)
491+
, ("rts.gc.current_bytes_used" , Gauge . Stats.currentBytesUsed)
492+
, ("rts.gc.current_bytes_slop" , Gauge . Stats.currentBytesSlop)
493+
, ("rts.gc.max_bytes_slop" , Gauge . Stats.maxBytesSlop)
494+
, ("rts.gc.peak_megabytes_allocated" , Gauge . Stats.peakMegabytesAllocated)
495+
, ("rts.gc.par_tot_bytes_copied" , Gauge . gcParTotBytesCopied)
496+
, ("rts.gc.par_avg_bytes_copied" , Gauge . gcParTotBytesCopied)
497+
, ("rts.gc.par_max_bytes_copied" , Gauge . Stats.parMaxBytesCopied)
503498
])
504499
getGcStats
505500
where
506501
-- | Convert seconds to milliseconds.
507-
sToMs :: Double -> Int
502+
sToMs :: Double -> Int64
508503
sToMs s = round (s * 1000.0)
509504
#endif
510505

@@ -619,7 +614,7 @@ getGcStats = Stats.getGCStats
619614
# endif
620615

621616
-- | Helper to work around rename in GHC.Stats in base-4.6.
622-
-- gcParTotBytesCopied :: Stats.GCStats -> Int64
617+
gcParTotBytesCopied :: Stats.GCStats -> Int64
623618
# if MIN_VERSION_base(4,6,0)
624619
gcParTotBytesCopied = Stats.parTotBytesCopied
625620
# else
@@ -663,8 +658,8 @@ sampleGroups cbSamplers = concat `fmap` sequence (map runOne cbSamplers)
663658
return $! map (\ (n, f) -> (n, f a)) (M.toList groupSamplerMetrics)
664659

665660
-- | The value of a sampled metric.
666-
data Value = Counter {-# UNPACK #-} !Int
667-
| Gauge {-# UNPACK #-} !Int
661+
data Value = Counter {-# UNPACK #-} !Int64
662+
| Gauge {-# UNPACK #-} !Int64
668663
| Label {-# UNPACK #-} !T.Text
669664
| Distribution !Distribution.Stats
670665
deriving (Eq, Show)

System/Metrics/Counter.hs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ module System.Metrics.Counter
1212
) where
1313

1414
import qualified Data.Atomic as Atomic
15+
import Data.Int (Int64)
1516
import Prelude hiding (read)
1617

1718
-- | A mutable, integer-valued counter.
@@ -22,13 +23,13 @@ new :: IO Counter
2223
new = C `fmap` Atomic.new 0
2324

2425
-- | Get the current value of the counter.
25-
read :: Counter -> IO Int
26+
read :: Counter -> IO Int64
2627
read = Atomic.read . unC
2728

2829
-- | Increase the counter by one.
2930
inc :: Counter -> IO ()
3031
inc counter = add counter 1
3132

3233
-- | Add the argument to the counter.
33-
add :: Counter -> Int -> IO ()
34+
add :: Counter -> Int64 -> IO ()
3435
add counter = Atomic.add (unC counter)

0 commit comments

Comments
 (0)