hw-dsv 0.2.1 → 0.3.0
raw patch · 21 files changed
+655/−273 lines, 21 filesdep +ghc-primdep +hw-simddep ~basedep ~hw-primPVP ok
version bump matches the API change (PVP)
Dependencies added: ghc-prim, hw-simd
Dependency ranges changed: base, hw-prim
API changes (from Hackage documentation)
- HaskellWorks.Data.Dsv.Lazy.Cursor: getRowBetween :: DsvCursor -> DsvCursor -> Bool -> Vector ByteString
- HaskellWorks.Data.Dsv.Lazy.Cursor.Internal: makeIbs :: Word64 -> Vector Word64 -> Vector Word64
+ HaskellWorks.Data.Dsv.Internal.Vector: constructNS :: forall a s. Storable a => Int -> s -> (s -> Vector a -> (s, a)) -> (s, Vector a)
+ HaskellWorks.Data.Dsv.Internal.Vector: indexCsvChunk :: Count -> Word64 -> Vector Word64 -> Vector Word64 -> Vector Word64 -> (Vector Word64, Vector Word64, Word64, Word64)
+ HaskellWorks.Data.Dsv.Internal.Vector: ltWord :: Word64 -> Word64 -> Word64
+ HaskellWorks.Data.Dsv.Internal.Vector: oddsMask :: Word64
+ HaskellWorks.Data.Dsv.Lazy.Cursor: advanceField :: Count -> DsvCursor -> DsvCursor
+ HaskellWorks.Data.Dsv.Lazy.Cursor: getRowBetweenStrict :: DsvCursor -> DsvCursor -> Bool -> Vector ByteString
+ HaskellWorks.Data.Dsv.Lazy.Cursor: selectListVector :: [Int] -> DsvCursor -> [[ByteString]]
+ HaskellWorks.Data.Dsv.Lazy.Cursor: toListVectorStrict :: DsvCursor -> [Vector ByteString]
Files
- README.md +26/−9
- app/App/Commands.hs +4/−3
- app/App/Commands/Cat.hs +0/−39
- app/App/Commands/CreateIndex.hs +19/−14
- app/App/Commands/Generate.hs +0/−2
- app/App/Commands/IndexWord8s.hs +66/−0
- app/App/Commands/Options/Type.hs +9/−1
- app/App/Commands/QueryLazy.hs +66/−3
- app/App/Commands/QueryStrict.hs +2/−2
- app/App/IO.hs +4/−0
- app/App/Lens.hs +1/−0
- bench/Main.hs +7/−7
- cbits/simd.c +112/−0
- cbits/simd.h +13/−0
- hw-dsv.cabal +162/−109
- src/HaskellWorks/Data/Dsv/Internal/Vector.hs +74/−0
- src/HaskellWorks/Data/Dsv/Lazy/Cursor.hs +86/−16
- src/HaskellWorks/Data/Dsv/Lazy/Cursor/Internal.hs +1/−47
- src/HaskellWorks/Data/Dsv/Strict/Cursor/Type.hs +1/−0
- test/HaskellWorks/Data/Dsv/Strict/Cursor/InternalSpec.hs +1/−3
- test/HaskellWorks/Data/DsvSpec.hs +1/−18
README.md view
@@ -4,7 +4,7 @@ Unbelievably fast streaming DSV file parser that reads based on succinct data structures. -This library will use support for some BMI2 CPU instructions on some x86 based+This library will use support for some BMI2 or AVX2 CPU instructions on some x86 based CPUs if compiled with the appropriate flags on `ghc-8.4.1` or later. ## Compilation@@ -23,15 +23,24 @@ stack bench ``` -For best performance, add the `bmi2` flag to target the BMI2 instruction set:+For best performance, add the `bmi2` and `avx2` flag to target the BMI2 and AVS2 instruction sets: ```text-stack build --flag bits-extra:bmi2 --flag hw-rankselect-base:bmi2 --flag hw-rankselect:bmi2 --flag hw-dsv:bmi2-stack test --flag bits-extra:bmi2 --flag hw-rankselect-base:bmi2 --flag hw-rankselect:bmi2 --flag hw-dsv:bmi2-stack bench --flag bits-extra:bmi2 --flag hw-rankselect-base:bmi2 --flag hw-rankselect:bmi2 --flag hw-dsv:bmi2-stack install --flag bits-extra:bmi2 --flag hw-rankselect-base:bmi2 --flag hw-rankselect:bmi2 --flag hw-dsv:bmi2+stack build --flag bits-extra:bmi2 --flag hw-rankselect-base:bmi2 --flag hw-rankselect:bmi2 --flag hw-simd:bmi2 --flag hw-simd:avx2 --flag hw-dsv:bmi2 --flag hw-dsv:avx2+stack test --flag bits-extra:bmi2 --flag hw-rankselect-base:bmi2 --flag hw-rankselect:bmi2 --flag hw-simd:bmi2 --flag hw-simd:avx2 --flag hw-dsv:bmi2 --flag hw-dsv:avx2+stack bench --flag bits-extra:bmi2 --flag hw-rankselect-base:bmi2 --flag hw-rankselect:bmi2 --flag hw-simd:bmi2 --flag hw-simd:avx2 --flag hw-dsv:bmi2 --flag hw-dsv:avx2+stack install --flag bits-extra:bmi2 --flag hw-rankselect-base:bmi2 --flag hw-rankselect:bmi2 --flag hw-simd:bmi2 --flag hw-simd:avx2 --flag hw-dsv:bmi2 --flag hw-dsv:avx2 ``` +For slightly older CPUs, add only the `bmi2` flag to target the BMI2 instruction set: ++```text+stack build --flag bits-extra:bmi2 --flag hw-rankselect-base:bmi2 --flag hw-rankselect:bmi2 --flag hw-simd:bmi2 --flag hw-dsv:bmi2+stack test --flag bits-extra:bmi2 --flag hw-rankselect-base:bmi2 --flag hw-rankselect:bmi2 --flag hw-simd:bmi2 --flag hw-dsv:bmi2+stack bench --flag bits-extra:bmi2 --flag hw-rankselect-base:bmi2 --flag hw-rankselect:bmi2 --flag hw-simd:bmi2 --flag hw-dsv:bmi2+stack install --flag bits-extra:bmi2 --flag hw-rankselect-base:bmi2 --flag hw-rankselect:bmi2 --flag hw-simd:bmi2 --flag hw-dsv:bmi2+```+ ## Benchmark results The following benchmark shows the kinds of performance gain that can@@ -47,12 +56,20 @@ 7.08GiB 0:07:25 [16.3MiB/s] ``` -With BMI2 enabled:+With BMI2 and AVX2 enabled: ```text+$ stack install --flag bits-extra:bmi2 --flag hw-bits:bmi2 --flag hw-rankselect-base:bmi2 --flag hw-rankselect:bmi2 --flag hw-dsv:bmi2 --flag hw-dsv:avx2+$ cat 7gb.csv | pv -t -e -b -a | hw-dsv query-lazy -k 0 -k 1 -d , -e '|' > /dev/null+7.08GiB 0:00:39 [ 181MiB/s]+```++With only BMI2 enabled:++```text $ stack install --flag bits-extra:bmi2 --flag hw-bits:bmi2 --flag hw-rankselect-base:bmi2 --flag hw-rankselect:bmi2 --flag hw-dsv:bmi2-$ cat 7g.csv | pv -t -e -b -a | hw-dsv query-lazy -k 0 -k 1 -d , -e '|' > /dev/null-7.08GiB 0:00:52 [ 138MiB/s]+$ cat 7gb.csv | pv -t -e -b -a | hw-dsv query-lazy -k 0 -k 1 -d , -e '|' > /dev/null+7.08GiB 0:00:43 [ 165MiB/s] ``` ## Using `hw-dsv` as a library
app/App/Commands.hs view
@@ -1,7 +1,8 @@ module App.Commands where -import App.Commands.Cat+import App.Commands.CreateIndex import App.Commands.Generate+import App.Commands.IndexWord8s import App.Commands.QueryLazy import App.Commands.QueryStrict import Data.Semigroup ((<>))@@ -13,13 +14,13 @@ commandsGeneral :: Parser (IO ()) commandsGeneral = subparser $ mempty <> commandGroup "Commands:"- -- <> cmdCreateIndex+ <> cmdCreateIndex <> cmdQueryLazy <> cmdQueryStrict commandsDebugging :: Parser (IO ()) commandsDebugging = subparser $ mempty <> commandGroup "Debugging commands:"- <> cmdCat+ <> cmdIndexWord8s <> cmdGenerate <> hidden
− app/App/Commands/Cat.hs
@@ -1,39 +0,0 @@-module App.Commands.Cat- ( cmdCat- ) where--import App.Commands.Options.Type-import Control.Lens-import Data.Semigroup ((<>))-import Options.Applicative hiding (columns)--import qualified App.IO as IO-import qualified App.Lens as L-import qualified Data.ByteString.Lazy as LBS--runCat :: CatOptions -> IO ()-runCat opts = do- let source = opts ^. L.source- let target = opts ^. L.target-- contents <- IO.readInputFile source-- LBS.writeFile target contents-- return ()--optsCat :: Parser CatOptions-optsCat = CatOptions- <$> strOption- ( long "input"- <> help "Input file"- <> metavar "FILE"- )- <*> strOption- ( long "output"- <> help "Output file"- <> metavar "FILE"- )--cmdCat :: Mod CommandFields (IO ())-cmdCat = command "cat" $ flip info idm $ runCat <$> optsCat
app/App/Commands/CreateIndex.hs view
@@ -8,15 +8,16 @@ import App.Commands.Options.Type import Control.Lens import Control.Monad-import Data.Semigroup ((<>))-import Options.Applicative hiding (columns)+import Data.Semigroup ((<>))+import HaskellWorks.Data.ByteString.Lazy+import Options.Applicative hiding (columns) -import qualified App.IO as IO-import qualified App.Lens as L-import qualified Data.ByteString.Builder as B-import qualified Data.Vector.Storable as DVS-import qualified HaskellWorks.Data.Dsv.Lazy.Cursor as SVL-import qualified System.IO as IO+import qualified App.IO as IO+import qualified App.Lens as L+import qualified Data.ByteString.Builder as B+import qualified Data.ByteString.Lazy as LBS+import qualified HaskellWorks.Data.Dsv.Lazy.Cursor as SVL+import qualified System.IO as IO runCreateIndex :: CreateIndexOptions -> IO () runCreateIndex opts = do@@ -28,15 +29,19 @@ let !cursor = SVL.makeCursor delimiter bs let !markers = cursor & SVL.dsvCursorMarkers let !newlines = cursor & SVL.dsvCursorNewlines+ let !filePathNoDash = if filePath == "-" then "stdin" else filePath - hOutMarkers <- IO.openFile (filePath ++ ".markers.idx") IO.WriteMode- forM_ (markers >>= DVS.toList) $ \w -> do- B.hPutBuilder hOutMarkers (B.word64LE w)+ hOutMarkers <- IO.openFile (filePathNoDash ++ ".markers.idx") IO.WriteMode+ hOutNewlines <- IO.openFile (filePathNoDash ++ ".newlines.idx") IO.WriteMode++ forM_ (zip markers newlines) $ \(markerV, newlineV) -> do+ LBS.hPut hOutMarkers (toLazyByteString markerV)+ LBS.hPut hOutNewlines (toLazyByteString newlineV)++ B.hPutBuilder hOutMarkers (B.word8 0xff) -- Telomere byte IO.hClose hOutMarkers - hOutNewlines <- IO.openFile (filePath ++ ".newlines.idx") IO.WriteMode- forM_ (newlines >>= DVS.toList) $ \w -> do- B.hPutBuilder hOutNewlines (B.word64LE w)+ B.hPutBuilder hOutNewlines (B.word8 0xff) -- Telomere byte IO.hClose hOutNewlines return ()
app/App/Commands/Generate.hs view
@@ -1,5 +1,3 @@-{-# LANGUAGE BangPatterns #-}- module App.Commands.Generate ( cmdGenerate ) where
+ app/App/Commands/IndexWord8s.hs view
@@ -0,0 +1,66 @@+module App.Commands.IndexWord8s+ ( cmdIndexWord8s+ ) where++import App.Commands.Options.Type+import Control.Lens+import Data.Semigroup ((<>))+import HaskellWorks.Data.Vector.AsVector64+import Options.Applicative++import qualified App.IO as IO+import qualified App.Lens as L+import qualified Data.ByteString.Lazy as LBS+import qualified HaskellWorks.Data.ByteString as BS+import qualified HaskellWorks.Data.ByteString.Lazy as LBS+import qualified HaskellWorks.Data.Simd.Comparison.Avx2 as AVX2+import qualified HaskellWorks.Data.Simd.Comparison.Stock as STOCK++runIndexWord8sNormal :: IndexWord8sOptions -> IO ()+runIndexWord8sNormal opts = do+ contents <- IO.readInputFile (opts ^. L.source)++ IO.writeOutputFile (opts ^. L.target) $ contents+ & LBS.toLazyByteString+ . fmap (STOCK.cmpEqWord8s 44 . asVector64)+ . BS.resegmentPadded 64+ . LBS.toChunks++runIndexWord8sSimd :: IndexWord8sOptions -> IO ()+runIndexWord8sSimd opts = do+ contents <- IO.readInputFile (opts ^. L.source)++ IO.writeOutputFile (opts ^. L.target) $ contents+ & LBS.toLazyByteString+ . fmap (AVX2.cmpEqWord8s 44 . asVector64)+ . BS.resegmentPadded 64+ . LBS.toChunks++runIndexWord8s :: IndexWord8sOptions -> IO ()+runIndexWord8s opts+ | opts ^. L.simd = runIndexWord8sSimd opts+ | otherwise = runIndexWord8sNormal opts++optsIndexWord8s :: Parser IndexWord8sOptions+optsIndexWord8s = IndexWord8sOptions+ <$> strOption+ ( long "input"+ <> help "Input file"+ <> metavar "FILE"+ <> showDefault+ <> value "-"+ )+ <*> strOption+ ( long "output"+ <> help "Output file"+ <> metavar "FILE"+ <> showDefault+ <> value "-"+ )+ <*> switch+ ( long "simd"+ <> help "Use simd method"+ )++cmdIndexWord8s :: Mod CommandFields (IO ())+cmdIndexWord8s = command "index-word8s" $ flip info idm $ runIndexWord8s <$> optsIndexWord8s
app/App/Commands/Options/Type.hs view
@@ -13,7 +13,7 @@ , _queryStrictOptionsOutputFilePath :: FilePath , _queryStrictOptionsDelimiter :: Word8 , _queryStrictOptionsOutDelimiter :: Word8- -- , _queryOptionsUseIndex :: Bool+ , _queryStrictOptionsUseIndex :: Bool } deriving (Eq, Show) data GenerateOptions = GenerateOptions@@ -24,12 +24,20 @@ data CatOptions = CatOptions { _catOptionsSource :: FilePath , _catOptionsTarget :: FilePath+ , _catOptionsSimd :: Bool } deriving (Eq, Show) +data IndexWord8sOptions = IndexWord8sOptions+ { _indexWord8sOptionsSource :: FilePath+ , _indexWord8sOptionsTarget :: FilePath+ , _indexWord8sOptionsSimd :: Bool+ } deriving (Eq, Show)+ data QueryLazyOptions = QueryLazyOptions { _queryLazyOptionsColumns :: [Int] , _queryLazyOptionsFilePath :: FilePath , _queryLazyOptionsOutputFilePath :: FilePath , _queryLazyOptionsDelimiter :: Word8 , _queryLazyOptionsOutDelimiter :: Word8+ , _queryLazyOptionsMethod :: String } deriving (Eq, Show)
app/App/Commands/QueryLazy.hs view
@@ -19,13 +19,28 @@ import qualified App.IO as IO import qualified App.Lens as L+import qualified Data.ByteString as BS import qualified Data.ByteString.Builder as B import qualified Data.ByteString.Lazy as LBS import qualified Data.Vector as DV import qualified HaskellWorks.Data.Dsv.Lazy.Cursor as SVL+import qualified System.Exit as IO+import qualified System.IO as IO +defaultMethod :: String+defaultMethod = "lazy-traverse"+ runQueryLazy :: QueryLazyOptions -> IO ()-runQueryLazy opts = do+runQueryLazy opts = case opts ^. L.method of+ "lazy-traverse" -> runQueryLazyFast opts+ "strict-traverse" -> runQueryLazySlow opts+ "strict-bytestring" -> runQueryLazyStrict opts+ method -> do+ IO.hPutStrLn IO.stderr $ "Unknown method: " <> method+ IO.exitFailure++runQueryLazySlow :: QueryLazyOptions -> IO ()+runQueryLazySlow opts = do !bs <- IO.readInputFile (opts ^. L.filePath) let !c = SVL.makeCursor (opts ^. L.delimiter) bs@@ -41,12 +56,52 @@ return () return ()- where columnToFieldString :: DV.Vector LBS.ByteString -> Int -> B.Builder columnToFieldString fields i = if i >= 0 && i < DV.length fields then B.lazyByteString (DV.unsafeIndex fields i)- else B.lazyByteString (LBS.empty)+ else B.lazyByteString LBS.empty +runQueryLazyStrict :: QueryLazyOptions -> IO ()+runQueryLazyStrict opts = do+ !bs <- IO.readInputFile (opts ^. L.filePath)++ let !c = SVL.makeCursor (opts ^. L.delimiter) bs+ let !rows = SVL.toListVectorStrict c+ let !outDelimiterBuilder = B.word8 (opts ^. L.outDelimiter)++ runResourceT $ do+ (_, hOut) <- IO.openOutputFile (opts ^. L.outputFilePath) Nothing+ forM_ rows $ \row -> do+ let fieldStrings = columnToFieldString row <$> (opts ^. L.columns)++ liftIO $ B.hPutBuilder hOut $ mconcat (intersperse outDelimiterBuilder fieldStrings) <> B.word8 10++ return ()+ return ()+ where columnToFieldString :: DV.Vector BS.ByteString -> Int -> B.Builder+ columnToFieldString fields i = if i >= 0 && i < DV.length fields+ then B.byteString (DV.unsafeIndex fields i)+ else B.byteString BS.empty++runQueryLazyFast :: QueryLazyOptions -> IO ()+runQueryLazyFast opts = do+ !bs <- IO.readInputFile (opts ^. L.filePath)++ let !c = SVL.makeCursor (opts ^. L.delimiter) bs+ let !sel = opts ^. L.columns+ let !rows = SVL.selectListVector sel c+ let !outDelimiterBuilder = B.word8 (opts ^. L.outDelimiter)++ runResourceT $ do+ (_, hOut) <- IO.openOutputFile (opts ^. L.outputFilePath) Nothing+ forM_ rows $ \row -> do+ let fieldStrings = fmap B.lazyByteString row++ liftIO $ B.hPutBuilder hOut $ mconcat (intersperse outDelimiterBuilder fieldStrings) <> B.word8 10++ return ()+ return ()+ cmdQueryLazy :: Mod CommandFields (IO ()) cmdQueryLazy = command "query-lazy" $ flip info idm $ runQueryLazy <$> optsQueryLazy @@ -85,4 +140,12 @@ <> short 'e' <> help "Output DSV delimiter" <> metavar "CHAR"+ )+ <*> strOption+ ( long "method"+ <> short 'm'+ <> help "Method"+ <> metavar "METHOD"+ <> showDefault+ <> value defaultMethod )
app/App/Commands/QueryStrict.hs view
@@ -28,7 +28,7 @@ runQueryStrict opts = do let delimiter = opts ^. L.delimiter let inputFilePath = opts ^. L.filePath- let useIndex = False -- opts ^. L.useIndex+ let useIndex = opts ^. L.useIndex c <- SVS.mmapCursor delimiter useIndex inputFilePath let !rows = SVS.toListVector c@@ -84,4 +84,4 @@ <> help "DSV delimiter to write in the output" <> metavar "CHAR" )- -- <*> switch (long "use-index")+ <*> switch (long "use-index")
app/App/IO.hs view
@@ -21,3 +21,7 @@ readInputFile :: FilePath -> IO LBS.ByteString readInputFile "-" = LBS.hGetContents IO.stdin readInputFile filePath = LBS.readFile filePath++writeOutputFile :: FilePath -> LBS.ByteString -> IO ()+writeOutputFile "-" bs = LBS.hPut IO.stdout bs+writeOutputFile filePath bs = LBS.writeFile filePath bs
app/App/Lens.hs view
@@ -11,6 +11,7 @@ import Control.Lens makeFields ''CatOptions+makeFields ''IndexWord8sOptions makeFields ''CreateIndexOptions makeFields ''GenerateOptions makeFields ''QueryLazyOptions
bench/Main.hs view
@@ -5,24 +5,24 @@ import Control.Monad import Criterion.Main-import Data.ByteString (ByteString)+import Data.ByteString (ByteString) import Data.List-import Data.Monoid ((<>))-import Data.Vector (Vector)+import Data.Monoid ((<>))+import Data.Vector (Vector) import Data.Word+import HaskellWorks.Data.Dsv.Internal.Char (comma, pipe) import System.Directory import qualified Data.ByteString.Lazy as LBS import qualified Data.Csv as CSV import qualified Data.Csv.Streaming as CSS import qualified Data.Vector.Storable as DVS-import HaskellWorks.Data.Dsv.Internal.Char (comma, pipe) import qualified HaskellWorks.Data.Dsv.Lazy.Cursor as SVL-import qualified HaskellWorks.Data.RankSelect.CsPoppy as RS import qualified HaskellWorks.Data.Dsv.Strict.Cursor as SVS import qualified HaskellWorks.Data.Dsv.Strict.Cursor.Internal as SVS import qualified HaskellWorks.Data.Dsv.Strict.Cursor.Internal.Reference as SVS import qualified HaskellWorks.Data.FromForeignRegion as IO+import qualified HaskellWorks.Data.RankSelect.CsPoppy as RS loadCassavaStrict :: FilePath -> IO (Vector (Vector ByteString)) loadCassavaStrict filePath = do@@ -99,6 +99,6 @@ main = do benchmarks <- (mconcat <$>) $ sequence $ mempty <> [makeBenchCsv]- <> [makeBenchW64s]- <> [makeBenchMkInterestBits]+ -- <> [makeBenchW64s]+ -- <> [makeBenchMkInterestBits] defaultMain benchmarks
+ cbits/simd.c view
@@ -0,0 +1,112 @@+#include "simd.h"++#include <immintrin.h>+#include <mmintrin.h>+#include <stdint.h>+#include <stdio.h>+#include <stdlib.h>+#include <string.h>+#include <unistd.h>+#include <ctype.h>++typedef uint8_t v32si __attribute__ ((vector_size (32)));+typedef uint8_t v16si __attribute__ ((vector_size (16)));++void print_bits_16(uint16_t word) {+ size_t i;++ putc('|', stdout);++ for (i = 0; i < 16; ++i) {+ putc((word & (1L << i)) ? '1' : '0', stdout);+ }+ printf("|\n");+}++void system_memcpy(+ char *target,+ char *source,+ size_t len) {+ memcpy(target, source, len);+}++void avx2_memcpy(+ uint8_t *target,+ uint8_t *source,+ size_t len) {+#if defined(AVX2_ENABLED)+ size_t aligned_len = (len / 32) * 32;+ size_t remaining_len = len - aligned_len;++ size_t i;++ for (i = 0; i < aligned_len; i += 32) {+ __m128i v0 = *(__m128i*)(source + i );+ __m128i v1 = *(__m128i*)(source + i + 16);++ *(__m128i*)(target + i ) = v0;+ *(__m128i*)(target + i + 16 ) = v1;+ }++ memcpy(target + aligned_len, source + aligned_len, remaining_len);+#endif+}+++void sse_cmpeq8(+ uint8_t byte,+ uint64_t *target,+ size_t target_length,+ uint8_t *source) {+ uint16_t *target16 = (uint16_t *)target;++ __m128i v_comparand = _mm_set1_epi8(byte);++ uint16_t *out_mask = (uint16_t*)target;+ size_t i;++ for (i = 0; i < target_length * 4; ++i) {+ __m128i v_data_a = *(__m128i*)(source + (i * 16));+ __m128i v_results_a = _mm_cmpeq_epi8(v_data_a, v_comparand);+ uint16_t mask = (uint16_t)_mm_movemask_epi8(v_results_a);+ target16[i] = mask;+ }+}++void avx2_cmpeq8(+ uint8_t byte,+ uint64_t *target,+ size_t target_length,+ uint8_t *source) {+#if defined(AVX2_ENABLED)+ uint32_t *target32 = (uint32_t *)target;++ __m256 v_comparand = _mm256_set1_epi8(byte);++ uint32_t *out_mask = (uint32_t*)target;++ size_t i;++ for (i = 0; i < target_length * 2; ++i) {+ __m256 v_data_a = *(__m256*)(source + (i * 32));+ __m256 v_results_a = _mm256_cmpeq_epi8(v_data_a, v_comparand);+ uint32_t mask = (uint32_t)_mm256_movemask_epi8(v_results_a);+ target32[i] = mask;+ }+#endif+}++int example_main() {+ uint8_t source[32] = "01234567890123456789012345678901";+ uint8_t target[33];+ avx2_memcpy(target, source, 32);+ target[32] = 0;+ printf("%s\n", target);++ // uint8_t source2[64] = "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef";+ // uint64_t target2[1] = {0};+ // avx2_cmpeq8('0', target2, 1, source2);+ // printf("%llu\n", target2[0]);++ return 0;+}
+ cbits/simd.h view
@@ -0,0 +1,13 @@+#include <unistd.h>+#include <stdint.h>++void avx2_memcpy(+ uint8_t *target,+ uint8_t *source,+ size_t len);++void avx2_cmpeq8(+ uint8_t byte,+ uint64_t *target,+ size_t target_length,+ uint8_t *source);
hw-dsv.cabal view
@@ -1,14 +1,12 @@--- This file has been generated from package.yaml by hpack version 0.27.0.+-- This file has been generated from package.yaml by hpack version 0.18.1. -- -- see: https://github.com/sol/hpack------ hash: 14ef3831452c80f0f1a586d2faec53d18ae39348940e4b1a47bc7dcb4d302e23 name: hw-dsv-version: 0.2.1+version: 0.3.0 synopsis: Unbelievably fast streaming DSV file parser description: Please see the README on Github at <https://github.com/haskell-works/hw-dsv#readme>-category: Text, Web, CSV+category: Text, CSV, SIMD, Succinct Data Structures, Data Structures homepage: https://github.com/haskell-works/hw-dsv#readme bug-reports: https://github.com/haskell-works/hw-dsv/issues author: John Ky@@ -21,6 +19,7 @@ cabal-version: >= 1.10 extra-source-files:+ cbits/simd.h ChangeLog.md data/bench/data-0001000.csv README.md@@ -29,6 +28,11 @@ type: git location: https://github.com/haskell-works/hw-dsv +flag avx2+ description: Enable avx2 instruction set+ manual: False+ default: False+ flag bmi2 description: Enable bmi2 instruction set manual: False@@ -40,11 +44,51 @@ default: True library+ hs-source-dirs:+ src+ ghc-options: -O2 -Wall+ include-dirs:+ cbits+ c-sources:+ cbits/simd.c+ build-depends:+ base >= 4.7 && < 5+ , bits-extra >= 0.0.1.2 && < 0.1+ , bytestring >= 0.10 && < 0.11+ , deepseq >= 1.4 && < 1.5+ , ghc-prim+ , hw-bits >= 0.7.0.2 && < 0.8+ , hw-rankselect >= 0.12.0.2 && < 0.13+ , hw-rankselect-base >= 0.3.2.0 && < 0.4+ , hw-prim >= 0.6.2.14 && < 0.7+ , hw-simd >= 0.1.1.2 && < 0.2+ , vector >= 0.12.0.1 && < 0.13+ build-tools:+ c2hs+ if flag(sse42)+ ghc-options: -msse4.2+ cc-options: -msse4.2+ if flag(bmi2)+ cc-options: -mbmi2 -DBMI2_ENABLED+ if (flag(bmi2)) && (impl(ghc >=8.4.1))+ ghc-options: -mbmi2 -msse4.2+ if flag(avx2)+ cc-options: -mavx2 -DAVX2_ENABLED+ if (flag(avx2)) && (impl(ghc >=8.4.1))+ ghc-options: -mbmi2 -msse4.2+ cpp-options: -DBMI2_ENABLED -DAVX2_ENABLED+ if (impl(ghc >=8.0.1))+ ghc-options: -Wcompat -Wincomplete-record-updates -Wincomplete-uni-patterns -Wredundant-constraints+ if (!impl(ghc >=8.0.1))+ build-depends:+ transformers >= 0.4 && < 0.6+ , semigroups >= 0.8.4 && < 0.19 exposed-modules: HaskellWorks.Data.Dsv.Internal.Bits HaskellWorks.Data.Dsv.Internal.Broadword HaskellWorks.Data.Dsv.Internal.Char HaskellWorks.Data.Dsv.Internal.Char.Word64+ HaskellWorks.Data.Dsv.Internal.Vector HaskellWorks.Data.Dsv.Lazy.Cursor HaskellWorks.Data.Dsv.Lazy.Cursor.Internal HaskellWorks.Data.Dsv.Lazy.Cursor.Type@@ -54,183 +98,192 @@ HaskellWorks.Data.Dsv.Strict.Cursor.Type other-modules: Paths_hw_dsv+ default-language: Haskell2010++executable hw-dsv+ main-is: Main.hs hs-source-dirs:- src- ghc-options: -O2 -Wall+ app+ ghc-options: -O2 -Wall -threaded -rtsopts -with-rtsopts=-N build-depends:- base >=4.7 && <5- , bits-extra >=0.0.1.2 && <0.1- , bytestring >=0.10 && <0.11- , deepseq >=1.4 && <1.5- , hw-bits >=0.7.0.2 && <0.8- , hw-prim >=0.6.2.0 && <0.7- , hw-rankselect >=0.12.0.2 && <0.13- , hw-rankselect-base >=0.3.2.0 && <0.4- , vector >=0.12.0.1 && <0.13+ base >= 4.7 && < 5+ , bits-extra >= 0.0.1.2 && < 0.1+ , bytestring >= 0.10 && < 0.11+ , deepseq >= 1.4 && < 1.5+ , ghc-prim+ , hw-bits >= 0.7.0.2 && < 0.8+ , hw-rankselect >= 0.12.0.2 && < 0.13+ , hw-rankselect-base >= 0.3.2.0 && < 0.4+ , hw-prim >= 0.6.2.14 && < 0.7+ , hw-simd >= 0.1.1.2 && < 0.2+ , vector >= 0.12.0.1 && < 0.13+ , hw-dsv+ , hedgehog >= 0.5 && < 0.7+ , lens >= 4.15 && < 5+ , optparse-applicative >= 0.13 && < 0.15+ , resourcet >= 1.1 && < 1.3 if flag(sse42) ghc-options: -msse4.2+ cc-options: -msse4.2+ if flag(bmi2)+ cc-options: -mbmi2 -DBMI2_ENABLED if (flag(bmi2)) && (impl(ghc >=8.4.1)) ghc-options: -mbmi2 -msse4.2+ if flag(avx2)+ cc-options: -mavx2 -DAVX2_ENABLED+ if (flag(avx2)) && (impl(ghc >=8.4.1))+ ghc-options: -mbmi2 -msse4.2+ cpp-options: -DBMI2_ENABLED -DAVX2_ENABLED if (impl(ghc >=8.0.1)) ghc-options: -Wcompat -Wincomplete-record-updates -Wincomplete-uni-patterns -Wredundant-constraints if (!impl(ghc >=8.0.1)) build-depends:- semigroups >=0.8.4 && <0.19- , transformers >=0.4 && <0.6- default-language: Haskell2010--executable hw-dsv- main-is: Main.hs+ transformers >= 0.4 && < 0.6+ , semigroups >= 0.8.4 && < 0.19 other-modules: App.Char App.Commands- App.Commands.Cat App.Commands.CreateIndex App.Commands.Generate+ App.Commands.IndexWord8s App.Commands.Options.Type App.Commands.QueryLazy App.Commands.QueryStrict App.Gen App.IO App.Lens- Paths_hw_dsv- hs-source-dirs:- app- ghc-options: -O2 -Wall -threaded -rtsopts -with-rtsopts=-N- build-depends:- base >=4.7 && <5- , bits-extra >=0.0.1.2 && <0.1- , bytestring >=0.10 && <0.11- , deepseq >=1.4 && <1.5- , hedgehog >=0.5 && <0.7- , hw-bits >=0.7.0.2 && <0.8- , hw-dsv- , hw-prim >=0.6.2.0 && <0.7- , hw-rankselect >=0.12.0.2 && <0.13- , hw-rankselect-base >=0.3.2.0 && <0.4- , lens >=4.15 && <5- , optparse-applicative >=0.13 && <0.15- , resourcet >=1.1 && <1.3- , vector >=0.12.0.1 && <0.13- if flag(sse42)- ghc-options: -msse4.2- if (flag(bmi2)) && (impl(ghc >=8.4.1))- ghc-options: -mbmi2 -msse4.2- if (impl(ghc >=8.0.1))- ghc-options: -Wcompat -Wincomplete-record-updates -Wincomplete-uni-patterns -Wredundant-constraints- if (!impl(ghc >=8.0.1))- build-depends:- semigroups >=0.8.4 && <0.19- , transformers >=0.4 && <0.6 default-language: Haskell2010 test-suite hw-dsv-space type: exitcode-stdio-1.0 main-is: Space.hs- other-modules:- Paths_hw_dsv hs-source-dirs: weigh ghc-options: -O2 -Wall build-depends:- base >=4.7 && <5- , bits-extra >=0.0.1.2 && <0.1- , bytestring >=0.10 && <0.11- , cassava >=0.5.1.0 && <0.6- , deepseq >=1.4 && <1.5- , hw-bits >=0.7.0.2 && <0.8+ base >= 4.7 && < 5+ , bits-extra >= 0.0.1.2 && < 0.1+ , bytestring >= 0.10 && < 0.11+ , deepseq >= 1.4 && < 1.5+ , ghc-prim+ , hw-bits >= 0.7.0.2 && < 0.8+ , hw-rankselect >= 0.12.0.2 && < 0.13+ , hw-rankselect-base >= 0.3.2.0 && < 0.4+ , hw-prim >= 0.6.2.14 && < 0.7+ , hw-simd >= 0.1.1.2 && < 0.2+ , vector >= 0.12.0.1 && < 0.13+ , cassava >= 0.5.1.0 && < 0.6 , hw-dsv- , hw-prim >=0.6.2.0 && <0.7- , hw-rankselect >=0.12.0.2 && <0.13- , hw-rankselect-base >=0.3.2.0 && <0.4- , vector >=0.12.0.1 && <0.13- , weigh >=0.0.6 && <0.1+ , weigh >= 0.0.6 && < 0.1 if flag(sse42) ghc-options: -msse4.2+ cc-options: -msse4.2+ if flag(bmi2)+ cc-options: -mbmi2 -DBMI2_ENABLED if (flag(bmi2)) && (impl(ghc >=8.4.1)) ghc-options: -mbmi2 -msse4.2+ if flag(avx2)+ cc-options: -mavx2 -DAVX2_ENABLED+ if (flag(avx2)) && (impl(ghc >=8.4.1))+ ghc-options: -mbmi2 -msse4.2+ cpp-options: -DBMI2_ENABLED -DAVX2_ENABLED if (impl(ghc >=8.0.1)) ghc-options: -Wcompat -Wincomplete-record-updates -Wincomplete-uni-patterns -Wredundant-constraints if (!impl(ghc >=8.0.1)) build-depends:- semigroups >=0.8.4 && <0.19- , transformers >=0.4 && <0.6+ transformers >= 0.4 && < 0.6+ , semigroups >= 0.8.4 && < 0.19 default-language: Haskell2010- build-tool-depends: hspec-discover:hspec-discover test-suite hw-dsv-test type: exitcode-stdio-1.0 main-is: Spec.hs- other-modules:- HaskellWorks.Data.Dsv.BroadwordSpec- HaskellWorks.Data.Dsv.Gen- HaskellWorks.Data.Dsv.Lazy.CursorSpec- HaskellWorks.Data.Dsv.Strict.Cursor.InternalSpec- HaskellWorks.Data.DsvSpec- Paths_hw_dsv hs-source-dirs: test ghc-options: -O2 -Wall -threaded -rtsopts -with-rtsopts=-N build-depends:- base >=4.7 && <5- , bits-extra >=0.0.1.2 && <0.1- , bytestring >=0.10 && <0.11- , deepseq >=1.4 && <1.5- , directory >=1.2.2 && <1.4- , hedgehog >=0.5 && <0.7- , hspec >=2.4 && <3- , hw-bits >=0.7.0.2 && <0.8+ base >= 4.7 && < 5+ , bits-extra >= 0.0.1.2 && < 0.1+ , bytestring >= 0.10 && < 0.11+ , deepseq >= 1.4 && < 1.5+ , ghc-prim+ , hw-bits >= 0.7.0.2 && < 0.8+ , hw-rankselect >= 0.12.0.2 && < 0.13+ , hw-rankselect-base >= 0.3.2.0 && < 0.4+ , hw-prim >= 0.6.2.14 && < 0.7+ , hw-simd >= 0.1.1.2 && < 0.2+ , vector >= 0.12.0.1 && < 0.13+ , directory >= 1.2.2 && < 1.4 , hw-dsv- , hw-hspec-hedgehog >=0.1.0.4 && <0.2- , hw-prim >=0.6.2.0 && <0.7- , hw-rankselect >=0.12.0.2 && <0.13- , hw-rankselect-base >=0.3.2.0 && <0.4- , text >=1.2.2 && <2.0- , vector >=0.12.0.1 && <0.13+ , hedgehog >= 0.5 && < 0.7+ , hspec >= 2.4 && < 3+ , hw-hspec-hedgehog >= 0.1.0.4 && < 0.2+ , text >= 1.2.2 && < 2.0 if flag(sse42) ghc-options: -msse4.2+ cc-options: -msse4.2+ if flag(bmi2)+ cc-options: -mbmi2 -DBMI2_ENABLED if (flag(bmi2)) && (impl(ghc >=8.4.1)) ghc-options: -mbmi2 -msse4.2+ if flag(avx2)+ cc-options: -mavx2 -DAVX2_ENABLED+ if (flag(avx2)) && (impl(ghc >=8.4.1))+ ghc-options: -mbmi2 -msse4.2+ cpp-options: -DBMI2_ENABLED -DAVX2_ENABLED if (impl(ghc >=8.0.1)) ghc-options: -Wcompat -Wincomplete-record-updates -Wincomplete-uni-patterns -Wredundant-constraints if (!impl(ghc >=8.0.1)) build-depends:- semigroups >=0.8.4 && <0.19- , transformers >=0.4 && <0.6+ transformers >= 0.4 && < 0.6+ , semigroups >= 0.8.4 && < 0.19+ other-modules:+ HaskellWorks.Data.Dsv.BroadwordSpec+ HaskellWorks.Data.Dsv.Gen+ HaskellWorks.Data.Dsv.Lazy.CursorSpec+ HaskellWorks.Data.Dsv.Strict.Cursor.InternalSpec+ HaskellWorks.Data.DsvSpec default-language: Haskell2010- build-tool-depends: hspec-discover:hspec-discover benchmark bench type: exitcode-stdio-1.0 main-is: Main.hs- other-modules:- Paths_hw_dsv hs-source-dirs: bench ghc-options: -O2 -Wall -msse4.2 build-depends:- base >=4.7 && <5- , bits-extra >=0.0.1.2 && <0.1- , bytestring >=0.10 && <0.11- , cassava >=0.5.1.0 && <0.6- , criterion >=1.4.1.0 && <1.5- , deepseq >=1.4 && <1.5- , directory >=1.3.1.5 && <1.4- , hw-bits >=0.7.0.2 && <0.8+ base >= 4.7 && < 5+ , bits-extra >= 0.0.1.2 && < 0.1+ , bytestring >= 0.10 && < 0.11+ , deepseq >= 1.4 && < 1.5+ , ghc-prim+ , hw-bits >= 0.7.0.2 && < 0.8+ , hw-rankselect >= 0.12.0.2 && < 0.13+ , hw-rankselect-base >= 0.3.2.0 && < 0.4+ , hw-prim >= 0.6.2.14 && < 0.7+ , hw-simd >= 0.1.1.2 && < 0.2+ , vector >= 0.12.0.1 && < 0.13+ , cassava >= 0.5.1.0 && < 0.6+ , criterion >= 1.4.1.0 && < 1.6+ , directory >= 1.3.1.5 && < 1.4 , hw-dsv- , hw-prim >=0.6.2.0 && <0.7- , hw-rankselect >=0.12.0.2 && <0.13- , hw-rankselect-base >=0.3.2.0 && <0.4- , mmap >=0.5.9 && <0.6- , vector >=0.12.0.1 && <0.13+ , mmap >= 0.5.9 && < 0.6 if flag(sse42) ghc-options: -msse4.2+ cc-options: -msse4.2+ if flag(bmi2)+ cc-options: -mbmi2 -DBMI2_ENABLED if (flag(bmi2)) && (impl(ghc >=8.4.1)) ghc-options: -mbmi2 -msse4.2+ if flag(avx2)+ cc-options: -mavx2 -DAVX2_ENABLED+ if (flag(avx2)) && (impl(ghc >=8.4.1))+ ghc-options: -mbmi2 -msse4.2+ cpp-options: -DBMI2_ENABLED -DAVX2_ENABLED if (impl(ghc >=8.0.1)) ghc-options: -Wcompat -Wincomplete-record-updates -Wincomplete-uni-patterns -Wredundant-constraints if (!impl(ghc >=8.0.1)) build-depends:- semigroups >=0.8.4 && <0.19- , transformers >=0.4 && <0.6+ transformers >= 0.4 && < 0.6+ , semigroups >= 0.8.4 && < 0.19 default-language: Haskell2010
+ src/HaskellWorks/Data/Dsv/Internal/Vector.hs view
@@ -0,0 +1,74 @@+{-# LANGUAGE MagicHash #-}+{-# LANGUAGE RankNTypes #-}+{-# LANGUAGE ScopedTypeVariables #-}++module HaskellWorks.Data.Dsv.Internal.Vector where++import Control.Monad.ST+import Data.Bits.Pdep+import Data.Word+import Foreign.Storable (Storable)+import GHC.Int+import GHC.Prim+import GHC.Word hiding (ltWord)+import HaskellWorks.Data.Bits.BitWise+import HaskellWorks.Data.Bits.PopCount.PopCount1+import HaskellWorks.Data.Positioning++import qualified Data.Vector.Storable as DVS+import qualified Data.Vector.Storable.Mutable as DVSM++constructNS :: forall a s. Storable a => Int -> s -> (s -> DVS.Vector a -> (s, a)) -> (s, DVS.Vector a)+constructNS n s f = DVS.createT (go 0 s)+ where go :: forall q. Int -> s -> ST q (s, DVS.MVector q a)+ go n1 s1 = do+ mv :: DVS.MVector q a <- DVSM.unsafeNew n+ u <- DVS.unsafeFreeze mv+ let (s2, w) = f s1 (DVS.take n1 u)+ DVSM.unsafeWrite mv n1 w+ return (s2, mv)+{-# INLINE constructNS #-}++ltWord :: Word64 -> Word64 -> Word64+ltWord (W64# a#) (W64# b#) = fromIntegral (I64# (ltWord# a# b#))+{-# INLINE ltWord #-}++indexCsvChunk ::+ Count+ -> Word64+ -> DVS.Vector Word64+ -> DVS.Vector Word64+ -> DVS.Vector Word64+ -> (DVS.Vector Word64, DVS.Vector Word64, Word64, Word64)+indexCsvChunk qqCount qqCarry mks nls qqs = runST $ do+ tmks <- DVSM.unsafeNew len+ tnls <- DVSM.unsafeNew len+ (newCount, newCarry) <- go 0 qqCount qqCarry tmks tnls+ rmks <- DVS.unsafeFreeze tmks+ rnls <- DVS.unsafeFreeze tnls+ return (rmks, rnls, newCount, newCarry)+ where len = DVS.length mks+ go :: Int -> Word64 -> Word64 -> DVSM.MVector z Word64 -> DVSM.MVector z Word64 -> ST z (Count, Word64)+ go i pc carry tmks tnls | i < len = do+ let qq = DVS.unsafeIndex qqs i+ let mk = DVS.unsafeIndex mks i+ let nl = DVS.unsafeIndex nls i++ let enters = pdep (oddsMask .<. (0x1 .&. pc)) qq+ let leaves = pdep (oddsMask .<. (0x1 .&. comp pc)) qq++ let compLeaves = comp leaves+ let preQuoteMask = enters + compLeaves+ let quoteMask = preQuoteMask + carry+ let newCarry = preQuoteMask `ltWord` enters++ DVSM.unsafeWrite tmks i ((nl .|. mk) .&. quoteMask)+ DVSM.unsafeWrite tnls i ( nl .&. quoteMask)++ go (i + 1) (popCount1 qq + pc) newCarry tmks tnls+ go _ pc carry _ _ = return (pc, carry)+{-# INLINE indexCsvChunk #-}++oddsMask :: Word64+oddsMask = 0x5555555555555555+{-# INLINE oddsMask #-}
src/HaskellWorks/Data/Dsv/Lazy/Cursor.hs view
@@ -5,28 +5,42 @@ , trim , atEnd , nextField+ , advanceField , nextRow , nextPosition- , getRowBetween , toListVector , toVectorVector+ , selectListVector+ , getRowBetweenStrict+ , toListVectorStrict ) where import Data.Function-import GHC.Word (Word8)-import HaskellWorks.Data.Dsv.Internal.Broadword (fillWord64)-import HaskellWorks.Data.Dsv.Internal.Bits-import HaskellWorks.Data.Dsv.Lazy.Cursor.Internal+import Data.Word+import GHC.Word (Word8) import HaskellWorks.Data.Dsv.Lazy.Cursor.Type+import HaskellWorks.Data.Positioning import HaskellWorks.Data.RankSelect.Base.Rank1 import HaskellWorks.Data.RankSelect.Base.Select1 import HaskellWorks.Data.Vector.AsVector64s import Prelude -import qualified Data.ByteString.Lazy as LBS-import qualified Data.Vector as DV-import qualified HaskellWorks.Data.Dsv.Internal.Char.Word64 as CW+import qualified Data.ByteString as BS+import qualified Data.ByteString.Lazy as LBS+import qualified Data.Vector as DV+import qualified Data.Vector.Storable as DVS+import qualified HaskellWorks.Data.Dsv.Internal.Char as C+import qualified HaskellWorks.Data.Dsv.Internal.Vector as DVS+import qualified HaskellWorks.Data.Simd.Comparison as DVS +makeIndexes :: [DVS.Vector Word64] -> [DVS.Vector Word64] -> [DVS.Vector Word64] -> ([DVS.Vector Word64], [DVS.Vector Word64])+makeIndexes ds ns qs = unzip $ go 0 0 ds ns qs+ where go pc carry (dv:dvs) (nv:nvs) (qv:qvs) =+ let (dv', nv', pc', carry') = DVS.indexCsvChunk pc carry dv nv qv in+ (dv', nv'):go pc' carry' dvs nvs qvs+ go _ _ [] [] [] = []+ go _ _ _ _ _ = error "Unbalanced inputs"+ makeCursor :: Word8 -> LBS.ByteString -> DsvCursor makeCursor delimiter lbs = DsvCursor { dsvCursorText = lbs@@ -35,14 +49,10 @@ , dsvCursorPosition = 0 } where ws = asVector64s 64 lbs- ibq = makeIbs CW.doubleQuote <$> ws- ibn = makeIbs CW.newline <$> ws- ibd = makeIbs (fillWord64 delimiter) <$> ws- pcq = makeCummulativePopCount ibq- ibr = zip2Or ibn ibd- qm = makeQuoteMask ibq pcq- ib = zip2And ibr qm- nls = zip2And ibn qm+ ibq = DVS.cmpEqWord8s C.doubleQuote <$> ws+ ibn = DVS.cmpEqWord8s C.newline <$> ws+ ibd = DVS.cmpEqWord8s delimiter <$> ws+ (ib, nls) = makeIndexes ibd ibn ibq {-# INLINE makeCursor #-} snippet :: DsvCursor -> LBS.ByteString@@ -76,6 +86,14 @@ newPos = select1 (dsvCursorMarkers cursor) (currentRank + 1) - 1 {-# INLINE nextField #-} +advanceField :: Count -> DsvCursor -> DsvCursor+advanceField n cursor = cursor+ { dsvCursorPosition = newPos+ }+ where currentRank = rank1 (dsvCursorMarkers cursor) (dsvCursorPosition cursor)+ newPos = select1 (dsvCursorMarkers cursor) (currentRank + n) - 1+{-# INLINE advanceField #-}+ nextRow :: DsvCursor -> DsvCursor nextRow cursor = cursor { dsvCursorPosition = if newPos > dsvCursorPosition cursor@@ -122,3 +140,55 @@ toVectorVector :: DsvCursor -> DV.Vector (DV.Vector LBS.ByteString) toVectorVector = DV.fromList . toListVector {-# INLINE toVectorVector #-}++selectRowFrom :: [Int] -> DsvCursor -> [LBS.ByteString]+selectRowFrom sel c = go <$> sel+ where go :: Int -> LBS.ByteString+ go n = snippet nc+ where nc = nextPosition (advanceField (fromIntegral n) c)+ {-# INLINE go #-}+{-# INLINE selectRowFrom #-}++selectListVector :: [Int] -> DsvCursor -> [[LBS.ByteString]]+selectListVector sel c = if dsvCursorPosition d > dsvCursorPosition c && not (atEnd c)+ then selectRowFrom sel c:selectListVector sel (trim d)+ else []+ where nr = nextRow c+ d = nextPosition nr+{-# INLINE selectListVector #-}++getRowBetweenStrict :: DsvCursor -> DsvCursor -> Bool -> DV.Vector BS.ByteString+getRowBetweenStrict c d dEnd = DV.unfoldrN fields go c+ where bsA = fromIntegral $ dsvCursorPosition c+ bsZ = fromIntegral $ dsvCursorPosition d+ bsT = dsvCursorText c+ bs = LBS.toStrict $ LBS.take (bsZ - bsA) (LBS.drop bsA bsT)++ cr = rank1 (dsvCursorMarkers c) (dsvCursorPosition c)+ dr = rank1 (dsvCursorMarkers d) (dsvCursorPosition d)+ c2d = fromIntegral (dr - cr)+ fields = if dEnd then c2d +1 else c2d+ go :: DsvCursor -> Maybe (BS.ByteString, DsvCursor)+ go e = case nextField e of+ f -> case nextPosition f of+ g -> case snippetStrict e (fromIntegral bsA) bs of+ s -> Just (s, g)+ {-# INLINE go #-}+{-# INLINE getRowBetweenStrict #-}++snippetStrict :: DsvCursor -> Int -> BS.ByteString -> BS.ByteString+snippetStrict c offset bs = BS.take (len `max` 0) $ BS.drop posC $ bs+ where d = nextField c+ posC = fromIntegral (dsvCursorPosition c) - offset+ posD = fromIntegral (dsvCursorPosition d) - offset+ len = posD - posC+{-# INLINE snippetStrict #-}++toListVectorStrict :: DsvCursor -> [DV.Vector BS.ByteString]+toListVectorStrict c = if dsvCursorPosition d > dsvCursorPosition c && not (atEnd c)+ then getRowBetweenStrict c d dEnd:toListVectorStrict (trim d)+ else []+ where nr = nextRow c+ d = nextPosition nr+ dEnd = atEnd nr+{-# INLINE toListVectorStrict #-}
src/HaskellWorks/Data/Dsv/Lazy/Cursor/Internal.hs view
@@ -2,65 +2,19 @@ {-# LANGUAGE FlexibleContexts #-} module HaskellWorks.Data.Dsv.Lazy.Cursor.Internal- ( makeIbs- , makeCummulativePopCount+ ( makeCummulativePopCount , makeQuoteMask ) where import Data.Word import HaskellWorks.Data.AtIndex-import HaskellWorks.Data.Bits.BitWise import HaskellWorks.Data.Bits.PopCount.PopCount1-import HaskellWorks.Data.Dsv.Internal.Bits import HaskellWorks.Data.Dsv.Internal.Broadword import Prelude import qualified Data.Vector.Storable as DVS {-# ANN module ("HLint: ignore Reduce duplication" :: String) #-}--makeIbs :: Word64 -> DVS.Vector Word64 -> DVS.Vector Word64-makeIbs iw v = DVS.constructN ((DVS.length v + 7) `div` 8) go- where go :: DVS.Vector Word64 -> Word64- go u = let ui = end u in- if ui * 8 + 8 < end v- then let vi = ui * 8- w0 = testWord8s ((v !!! (vi + 0)) .^. iw)- w1 = testWord8s ((v !!! (vi + 1)) .^. iw)- w2 = testWord8s ((v !!! (vi + 2)) .^. iw)- w3 = testWord8s ((v !!! (vi + 3)) .^. iw)- w4 = testWord8s ((v !!! (vi + 4)) .^. iw)- w5 = testWord8s ((v !!! (vi + 5)) .^. iw)- w6 = testWord8s ((v !!! (vi + 6)) .^. iw)- w7 = testWord8s ((v !!! (vi + 7)) .^. iw)- w = (w7 .<. 56) .|.- (w6 .<. 48) .|.- (w5 .<. 40) .|.- (w4 .<. 32) .|.- (w3 .<. 24) .|.- (w2 .<. 16) .|.- (w1 .<. 8) .|.- w0- in comp w- else let vi = ui * 8- w0 = testWord8s (atIndexOr 0 v (vi + 0) .^. iw)- w1 = testWord8s (atIndexOr 0 v (vi + 1) .^. iw)- w2 = testWord8s (atIndexOr 0 v (vi + 2) .^. iw)- w3 = testWord8s (atIndexOr 0 v (vi + 3) .^. iw)- w4 = testWord8s (atIndexOr 0 v (vi + 4) .^. iw)- w5 = testWord8s (atIndexOr 0 v (vi + 5) .^. iw)- w6 = testWord8s (atIndexOr 0 v (vi + 6) .^. iw)- w7 = testWord8s (atIndexOr 0 v (vi + 7) .^. iw)- w = (w7 .<. 56) .|.- (w6 .<. 48) .|.- (w5 .<. 40) .|.- (w4 .<. 32) .|.- (w3 .<. 24) .|.- (w2 .<. 16) .|.- (w1 .<. 8) .|.- w0- in comp w-{-# INLINE makeIbs #-} makeQuoteMask1 :: DVS.Vector Word64 -> DVS.Vector Word64 -> DVS.Vector Word64 makeQuoteMask1 ibv pcv = DVS.constructN (DVS.length ibv) go
src/HaskellWorks/Data/Dsv/Strict/Cursor/Type.hs view
@@ -1,6 +1,7 @@ {-# LANGUAGE DeriveGeneric #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE StandaloneDeriving #-}+{-# LANGUAGE UndecidableInstances #-} module HaskellWorks.Data.Dsv.Strict.Cursor.Type ( DsvCursor(..)
test/HaskellWorks/Data/Dsv/Strict/Cursor/InternalSpec.hs view
@@ -11,6 +11,7 @@ import Data.List (isSuffixOf) import Data.Monoid ((<>)) import HaskellWorks.Data.Bits.PopCount.PopCount1+import HaskellWorks.Data.Dsv.Internal.Char (pipe) import HaskellWorks.Data.FromByteString import HaskellWorks.Hspec.Hedgehog import Hedgehog@@ -20,7 +21,6 @@ import qualified Data.Text as T import qualified Data.Text.Encoding as T import qualified Data.Vector.Storable as DVS-import HaskellWorks.Data.Dsv.Internal.Char (comma, pipe) import qualified HaskellWorks.Data.Dsv.Strict.Cursor.Internal as SVS import qualified HaskellWorks.Data.Dsv.Strict.Cursor.Internal.Reference as SVS import qualified HaskellWorks.Data.FromForeignRegion as IO@@ -64,10 +64,8 @@ it "Case 5" $ requireTest $ do bs :: ByteString <- forAll $ T.encodeUtf8 . T.pack <$> G.string (R.linear 0 10000) (G.element " \"") v <- forAll $ pure $ fromByteString bs- numQuotes <- forAll $ pure $ BS.length $ BS.filter (== fromIntegral (ord '"')) bs u <- forAll $ pure $ fst $ SVS.makeIndexes pipe v let !expected = SVS.mkIbVector pipe v- let !pc = DVS.foldr (\a b -> popCount1 a + b) 0 u u === expected it "Case 6" $ requireTest $ do entries <- liftIO $ IO.listDirectory "data/bench"
test/HaskellWorks/Data/DsvSpec.hs view
@@ -4,15 +4,13 @@ module HaskellWorks.Data.DsvSpec (spec) where -import Data.Word-import HaskellWorks.Data.Bits.BitRead import HaskellWorks.Data.Bits.BitShow+import HaskellWorks.Data.Dsv.Internal.Char (comma) import HaskellWorks.Data.FromByteString import HaskellWorks.Hspec.Hedgehog import Hedgehog import Test.Hspec -import HaskellWorks.Data.Dsv.Internal.Char (comma) import qualified HaskellWorks.Data.Dsv.Strict.Cursor.Internal.Reference as SVS {-# ANN module ("HLint: ignore Redundant do" :: String) #-}@@ -26,11 +24,6 @@ \12345678,12345678,123456,abcdefghijklmnopqrstuvwxyz\n\ \12345678,12345678,123456,abcdefghijklmnopqrstuvwxyz\n\ \12345678,12345678,123456,abcdefghijklmnopqrstuvwxyz"- let Just (expected :: [Word64]) = bitRead- "00000000 10000000 01000000 10000000 00000000 00000000 00010000 00001000 \- \00000100 00001000 00000000 00000000 00000001 00000000 10000000 01000000 \- \10000000 00000000 00000000 00010000 00001000 00000100 00001000 00000000 \- \00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000" let v = fromByteString bs let actual = SVS.mkIbVector comma v@@ -44,11 +37,6 @@ \12345678,12345678,123456,\"bcdefghijklmnopqrstuvwxy\"\n\ \12345678,12345678,123456,\"bcdefghijklmnopqrstuvwxy\"\n\ \12345678,12345678,123456,\"bcdefghijklmnopqrstuvwxy\""- let Just (expected :: [Word64]) = bitRead- "00000000 10000000 01000000 10000000 00000000 00000000 00010000 00001000 \- \00000100 00001000 00000000 00000000 00000001 00000000 10000000 01000000 \- \10000000 00000000 00000000 00010000 00001000 00000100 00001000 00000000 \- \00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000" let v = fromByteString bs let !actual = SVS.mkIbVector comma v@@ -62,11 +50,6 @@ \12345678,12345678,123456,\"bcdefghijklm,opqrstuvwxy\"\n\ \12345678,12345678,123456,\"bcdefghijklm,opqrstuvwxy\"\n\ \12345678,12345678,123456,\"bcdefghijklm,opqrstuvwxy\""- let Just (expected :: [Word64]) = bitRead- "00000000 10000000 01000000 10000000 00000000 00000000 00010000 00001000 \- \00000100 00001000 00000000 00000000 00000001 00000000 10000000 01000000 \- \10000000 00000000 00000000 00010000 00001000 00000100 00001000 00000000 \- \00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000" let v = fromByteString bs let !actual = SVS.mkIbVector comma v