Skip to content

Commit b186cfa

Browse files
committed
Use memcpy over byte-byte comparision in RingArray
1 parent 4f53e4d commit b186cfa

File tree

6 files changed

+62
-52
lines changed

6 files changed

+62
-52
lines changed

core/src/Streamly/Internal/Data/MutArray/Lib.c

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,3 +13,8 @@ size_t memchr_index(const void *dst, size_t off, int c, size_t len) {
1313
return len;
1414
}
1515
}
16+
17+
int memcmp_index(const void *p1, size_t p1_off, const void *p2, size_t p2_off, size_t len) {
18+
int cmp = memcmp((char *)p1 + p1_off, (char *)p2 + p2_off, len);
19+
return cmp;
20+
}

core/src/Streamly/Internal/Data/MutArray/Type.hs

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -295,6 +295,9 @@ module Streamly.Internal.Data.MutArray.Type
295295
, isPower2
296296
, roundUpToPower2
297297

298+
-- * Foreign APIs
299+
, c_memcmp_index
300+
298301
-- * Deprecated
299302
, realloc
300303
, createOfWith
@@ -368,7 +371,7 @@ import Data.Char (ord)
368371
import Data.Functor.Identity (Identity(..))
369372
import Data.Proxy (Proxy(..))
370373
import Data.Word (Word8, Word16)
371-
import Foreign.C.Types (CSize(..))
374+
import Foreign.C.Types (CSize(..), CInt(..))
372375
import Foreign.Ptr (plusPtr)
373376
import Streamly.Internal.Data.MutByteArray.Type
374377
( MutByteArray(..)
@@ -427,6 +430,11 @@ foreign import ccall unsafe "memchr_index" c_memchr_index
427430
foreign import ccall unsafe "string.h strlen" c_strlen
428431
:: Ptr Word8 -> IO CSize
429432

433+
foreign import ccall unsafe "memcmp_index" c_memcmp_index
434+
:: MutableByteArray# RealWorld -> CSize
435+
-> MutableByteArray# RealWorld -> CSize
436+
-> CSize -> IO CInt
437+
430438
-- | Given an 'Unboxed' type (unused first arg) and a number of bytes, return
431439
-- how many elements of that type will completely fit in those bytes.
432440
--

core/src/Streamly/Internal/Data/RingArray.hs

Lines changed: 26 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -639,31 +639,24 @@ cast ring =
639639
eqArrayN :: RingArray a -> Array a -> Int -> IO Bool
640640
eqArrayN RingArray{..} Array.Array{..} nBytes
641641
| nBytes < 0 = error "eqArrayN: n should be >= 0"
642-
| arrLen < nBytes = error "eqArrayN: array is shorter than n"
642+
| arrEnd - arrStart < nBytes = error "eqArrayN: array is shorter than n"
643643
| ringSize < nBytes = error "eqArrayN: ring is shorter than n"
644644
| nBytes == 0 = return True
645-
| otherwise = check ringHead 0
646-
645+
| nBytesC <= p1Len = do
646+
part1 <- MutArray.c_memcmp_index arr# 0 ring# p1Off nBytesC
647+
pure $ part1 == 0
648+
| otherwise = do
649+
part1 <- MutArray.c_memcmp_index arr# 0 ring# p1Off p1Len
650+
part2 <- MutArray.c_memcmp_index arr# p1Len ring# p2Off p2Len
651+
pure $ part1 == 0 && part2 == 0
647652
where
648-
649-
arrLen = arrEnd - arrStart
650-
651-
-- XXX compare Word64 at a time
652-
check ringIndex arrayIndex = do
653-
(relem :: Word8) <- peekAt ringIndex ringContents
654-
aelem <- peekAt arrayIndex arrContents
655-
if relem == aelem
656-
then go (ringIndex + 1) (arrayIndex + 1)
657-
else return False
658-
659-
go ringIndex arrayIndex
660-
-- Checking ringIndex == rh is enough
661-
-- | arrayIndex == nBytes = return True
662-
| ringIndex == ringSize = go 0 arrayIndex
663-
| ringIndex == ringHead = return True
664-
| otherwise = check ringIndex arrayIndex
665-
666-
-- XXX We can use memcmp over two segments.
653+
nBytesC = fromIntegral nBytes
654+
arr# = MutByteArray.getMutByteArray# arrContents
655+
ring# = MutByteArray.getMutByteArray# ringContents
656+
p1Off = fromIntegral ringHead
657+
p1Len = fromIntegral $ ringSize - ringHead
658+
p2Off = 0
659+
p2Len = nBytesC - p1Len
667660

668661
-- | Byte compare the entire length of ringBuffer with the given array,
669662
-- starting at the supplied ring head index. Returns true if the Array and
@@ -674,25 +667,18 @@ eqArrayN RingArray{..} Array.Array{..} nBytes
674667
{-# INLINE eqArray #-}
675668
eqArray :: RingArray a -> Array a -> IO Bool
676669
eqArray RingArray{..} Array.Array{..}
677-
| arrLen < ringSize = error "eqArrayN: array is shorter than ring"
678-
| otherwise = check ringHead 0
679-
670+
| arrEnd - arrStart < ringSize = error "eqArrayN: array is shorter than ring"
671+
| otherwise = do
672+
part1 <- MutArray.c_memcmp_index arr# 0 ring# p1Off p1Len
673+
part2 <- MutArray.c_memcmp_index arr# p1Len ring# p2Off p2Len
674+
pure $ part1 == 0 && part2 == 0
680675
where
681-
682-
arrLen = arrEnd - arrStart
683-
684-
-- XXX compare Word64 at a time
685-
check ringIndex arrayIndex = do
686-
(relem :: Word8) <- peekAt ringIndex ringContents
687-
aelem <- peekAt arrayIndex arrContents
688-
if relem == aelem
689-
then go (ringIndex + 1) (arrayIndex + 1)
690-
else return False
691-
692-
go ringIndex arrayIndex
693-
| ringIndex == ringSize = go 0 arrayIndex
694-
| ringIndex == ringHead = return True
695-
| otherwise = check ringIndex arrayIndex
676+
arr# = MutByteArray.getMutByteArray# arrContents
677+
ring# = MutByteArray.getMutByteArray# ringContents
678+
p1Off = fromIntegral ringHead
679+
p1Len = fromIntegral $ ringSize - ringHead
680+
p2Off = 0
681+
p2Len = fromIntegral $ ringHead
696682

697683
-------------------------------------------------------------------------------
698684
-- Folding

test/Streamly/Test/Data/Array.hs

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import Streamly.Internal.Data.MutByteArray (Unbox, sizeOf)
2020
import Streamly.Internal.Data.MutArray (MutArray)
2121
import Test.QuickCheck (chooseInt, listOf)
2222
import System.Mem (performMajorGC)
23+
import Streamly.Test.Common (performGCSweep)
2324

2425
import qualified Streamly.Internal.Data.Array as A
2526
import qualified Streamly.Internal.Data.MutArray as MA
@@ -223,12 +224,6 @@ testUnsafeAsForeignPtr = do
223224
where
224225
getIntList1 fp blen = withForeignPtr fp $ \p -> getIntList p blen
225226

226-
performGCSweep :: Int -> Int -> IO ()
227-
performGCSweep i j = do
228-
mapM_ id $ replicate i $ do
229-
performMajorGC
230-
void $ MA.fromList ([0 .. j] :: [Int])
231-
232227
testForeignPtrConversionId :: IO ()
233228
testForeignPtrConversionId = do
234229
arr0 <- MA.unsafeGetSlice 10 50 <$> MA.fromList ([0 .. 99] :: [Word8])

test/Streamly/Test/Data/RingArray.hs

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,29 +8,34 @@
88

99
module Streamly.Test.Data.RingArray (main) where
1010

11+
import Streamly.Test.Common (performGCSweep)
12+
1113
import qualified Streamly.Internal.Data.MutArray as MutArray
1214
import qualified Streamly.Internal.Data.Array as Array
1315
import qualified Streamly.Internal.Data.RingArray as RingArray
1416

1517
import Prelude as P
16-
1718
import Test.Hspec as H
1819

1920
eqArrayN :: [Int] -> [Int] -> Int -> Int -> Bool -> IO ()
20-
eqArrayN lstArr lstRing startR nelem expected = do
21+
eqArrayN lstArr lstRing startR nBytes expected = do
2122
let arr = Array.fromList lstArr
2223
marr <- MutArray.fromList lstRing
2324
let ring =
2425
maybe (error "cast failed") id $ RingArray.castMutArrayWith startR marr
25-
RingArray.eqArrayN ring arr nelem `shouldReturn` expected
26+
performGCSweep 4 100000
27+
res <- RingArray.eqArrayN ring arr nBytes
28+
res `shouldBe` expected
2629

2730
eqArray :: [Int] -> [Int] -> Int -> Bool -> IO ()
2831
eqArray lstArr lstRing startR expected = do
2932
let arr = Array.fromList lstArr
3033
marr <- MutArray.fromList lstRing
3134
let ring =
3235
maybe (error "cast failed") id $ RingArray.castMutArrayWith startR marr
33-
RingArray.eqArray ring arr `shouldReturn` expected
36+
performGCSweep 4 100000
37+
res <- RingArray.eqArray ring arr
38+
res `shouldBe` expected
3439

3540
moduleName :: String
3641
moduleName = "Data.RingArray"

test/lib/Streamly/Test/Common.hs

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,14 +13,18 @@ module Streamly.Test.Common
1313
, checkListEqual
1414
, chooseInt
1515
, chooseDouble
16+
, performGCSweep
1617
) where
1718

18-
import Control.Monad (when)
19+
import Control.Monad (when, void)
1920
import Control.Monad.IO.Class (MonadIO(..))
2021
import Data.List ((\\))
22+
import System.Mem (performMajorGC)
2123
import Test.QuickCheck (Property, Gen, choose, counterexample)
2224
import Test.QuickCheck.Monadic (PropertyM, assert, monitor, monadicIO)
2325

26+
import qualified Streamly.Internal.Data.MutArray as MA
27+
2428
equals
2529
:: (Show a, Monad m)
2630
=> (a -> a -> Bool) -> a -> a -> PropertyM m ()
@@ -64,3 +68,10 @@ chooseInt = choose
6468

6569
chooseDouble :: (Double, Double) -> Gen Double
6670
chooseDouble = choose
71+
72+
-- XXX Move this to Streamly.Test.Array.Common?
73+
performGCSweep :: Int -> Int -> IO ()
74+
performGCSweep i j = do
75+
mapM_ id $ replicate i $ do
76+
performMajorGC
77+
void $ MA.fromList ([0 .. j] :: [Int])

0 commit comments

Comments
 (0)