Skip to content

Commit 02866e2

Browse files
committed
feat: parallel parquet reader, countDistinct and packed-text kernels,
1 parent 706ab18 commit 02866e2

66 files changed

Lines changed: 2946 additions & 829 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

‎dataframe-arrow-bridge/src/DataFrame/IO/Arrow.hs‎

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@ import qualified Data.Text.Encoding as TE
2222
import qualified Data.Vector as V
2323
import qualified Data.Vector.Unboxed as VU
2424
import qualified DataFrame.Internal.Column as DI
25-
import qualified DataFrame.Internal.Column.Bitmap as DI
2625

2726
import Control.Monad (foldM_, forM, join, when, zipWithM_)
2827
import Data.Type.Equality (TestEquality (testEquality), type (:~:) (Refl))

‎dataframe-core/dataframe-core.cabal‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ library
4646
DataFrame.Internal.Control.Concurrent
4747
-- Aggregation kernels and planning.
4848
DataFrame.Internal.Aggregation.Kernel.Dense
49+
DataFrame.Internal.Aggregation.Kernel.Distinct
4950
DataFrame.Internal.Aggregation.Kernel.Fused
5051
DataFrame.Internal.Aggregation.Kernel.Moments
5152
DataFrame.Internal.Aggregation.Kernel.Scatter

‎dataframe-core/src-internal/DataFrame/Internal/Aggregation/Kernel/Dense.hs‎

Lines changed: 2 additions & 156 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@ module DataFrame.Internal.Aggregation.Kernel.Dense (
2323
denseMaxMinusMin,
2424
) where
2525

26-
import Control.Monad (when)
2726
import Data.Type.Equality (TestEquality (..), type (:~:) (Refl))
2827
import qualified Data.Vector.Unboxed as VU
2928
import qualified Data.Vector.Unboxed.Mutable as VUM
@@ -74,9 +73,7 @@ denseReduce red g nGroups col = case col of
7473
{-# INLINEABLE denseReduce #-}
7574

7675
{- | The reductions admitted over an Int column. Sum/min/max/mean/count are
77-
exact in the Int domain (any merge order gives the same bits); top2sum selects
78-
the two largest values (order-independent as a multiset selection) and only
79-
adds them once at finalize. Var/std stay with the group-range gather kernel:
76+
exact in the Int domain (any merge order gives the same bits). Var/std stay with the group-range gather kernel:
8077
its per-group Welford recurrence replays the interpreter's update order
8178
bit-for-bit, which no chunk-merged direct pass can.
8279
-}
@@ -87,11 +84,9 @@ denseInt red g nGroups v = case red of
8784
RMin -> Just (fromUnboxedVector (extremaIntDense True g nGroups v))
8885
RMax -> Just (fromUnboxedVector (extremaIntDense False g nGroups v))
8986
RMean -> Just (fromUnboxedVector (meanIntDense g nGroups v))
90-
RTop2Sum -> Just (fromUnboxedVector (top2Dense g nGroups v))
91-
RTop2Snd -> Just (fromUnboxedVector (top2SndDense g nGroups v))
9287
_ -> Nothing
9388

94-
{- | The reductions admitted over a Double column. Count/min/max/top2sum are
89+
{- | The reductions admitted over a Double column. Count/min/max are
9590
order-independent (exact per-worker merge, byte-identical at any @-N@). The
9691
float sum/mean run sequentially in row order below 'seqFloatGroups' (matching
9792
the gather kernel's per-group addition order exactly) and as deterministic
@@ -105,8 +100,6 @@ denseDouble red g nGroups v = case red of
105100
RMean -> Just (fromUnboxedVector (meanDblDense g nGroups v))
106101
RMin -> Just (fromUnboxedVector (extremaDblDense True g nGroups v))
107102
RMax -> Just (fromUnboxedVector (extremaDblDense False g nGroups v))
108-
RTop2Sum -> Just (fromUnboxedVector (top2Dense g nGroups v))
109-
RTop2Snd -> Just (fromUnboxedVector (top2SndDense g nGroups v))
110103
_ -> Nothing
111104

112105
{- | The fused @max a - min b@ direct pass: BOTH extrema accumulate in one
@@ -497,153 +490,6 @@ finalizeMaxMinusMin nGroups mx mn = do
497490
go 0
498491
VU.unsafeFreeze out
499492

500-
-------------------------------------------------------------------------------
501-
-- Top-2 sum (order-independent multiset selection; one float add at finalize)
502-
-------------------------------------------------------------------------------
503-
504-
{- | Sum of the two largest values per group. Each accumulator holds the
505-
(largest, second-largest) pair seen so far; merging two pairs keeps the top two
506-
of the four candidates. No float ADDITION happens until the single @m1 + m2@ at
507-
finalize, so the result is byte-identical to the gather kernel regardless of
508-
chunking. Mirrors the gather kernel exactly, including the @realToFrac@ per
509-
element and the @-inf -> 0@ guards at finalize.
510-
-}
511-
top2Dense ::
512-
(VU.Unbox a, Real a) => VU.Vector Int -> Int -> VU.Vector a -> VU.Vector Double
513-
{-# SPECIALIZE top2Dense ::
514-
VU.Vector Int -> Int -> VU.Vector Int -> VU.Vector Double
515-
#-}
516-
{-# SPECIALIZE top2Dense ::
517-
VU.Vector Int -> Int -> VU.Vector Double -> VU.Vector Double
518-
#-}
519-
top2Dense g nGroups v
520-
| not (shouldPar n) = unsafePerformIO $ do
521-
(m1, m2) <- top2Chunk g v nGroups 0 n
522-
finalizeTop2 nGroups m1 m2
523-
| otherwise = unsafePerformIO $ do
524-
parts <- runPartialsOver n capabilities (top2Chunk g v nGroups)
525-
(m1, m2) <- mergeTop2 nGroups parts
526-
finalizeTop2 nGroups m1 m2
527-
where
528-
!n = VU.length v
529-
{- INLINEABLE (not NOINLINE) so the SPECIALIZE pragmas above take effect; pure
530-
function of its arguments, so unsafePerformIO sharing is not a concern. -}
531-
{-# INLINEABLE top2Dense #-}
532-
533-
{- | Second-largest value per group: the exact same per-worker
534-
(largest, second-largest) accumulator and merge as 'top2Dense'
535-
('top2Chunk'/'mergeTop2'), finalized to the second max alone. A group of
536-
size < 2 finalizes its @-inf@ seed to NaN — documented behaviour (the
537-
db-benchmark Q8 data has no size-1 @id6@ groups). Order-independent multiset
538-
selection, so byte-identical at any @-N@.
539-
-}
540-
top2SndDense ::
541-
(VU.Unbox a, Real a) => VU.Vector Int -> Int -> VU.Vector a -> VU.Vector Double
542-
{-# SPECIALIZE top2SndDense ::
543-
VU.Vector Int -> Int -> VU.Vector Int -> VU.Vector Double
544-
#-}
545-
{-# SPECIALIZE top2SndDense ::
546-
VU.Vector Int -> Int -> VU.Vector Double -> VU.Vector Double
547-
#-}
548-
top2SndDense g nGroups v
549-
| not (shouldPar n) = unsafePerformIO $ do
550-
(m1, m2) <- top2Chunk g v nGroups 0 n
551-
finalizeTop2Snd nGroups m1 m2
552-
| otherwise = unsafePerformIO $ do
553-
parts <- runPartialsOver n capabilities (top2Chunk g v nGroups)
554-
(m1, m2) <- mergeTop2 nGroups parts
555-
finalizeTop2Snd nGroups m1 m2
556-
where
557-
!n = VU.length v
558-
{- INLINEABLE (not NOINLINE) so the SPECIALIZE pragmas above take effect; pure
559-
function of its arguments, so unsafePerformIO sharing is not a concern. -}
560-
{-# INLINEABLE top2SndDense #-}
561-
562-
top2Chunk ::
563-
(VU.Unbox a, Real a) =>
564-
VU.Vector Int ->
565-
VU.Vector a ->
566-
Int ->
567-
Int ->
568-
Int ->
569-
IO (VUM.IOVector Double, VUM.IOVector Double)
570-
top2Chunk g v nGroups lo hi = do
571-
let ninf = negate (1 / 0) :: Double
572-
m1 <- VUM.replicate nGroups ninf
573-
m2 <- VUM.replicate nGroups ninf
574-
let go !i
575-
| i >= hi = pure ()
576-
| otherwise = do
577-
let !k = VU.unsafeIndex g i
578-
!x = realToFrac (VU.unsafeIndex v i)
579-
a1 <- VUM.unsafeRead m1 k
580-
if x > a1
581-
then do
582-
VUM.unsafeWrite m1 k x
583-
VUM.unsafeWrite m2 k a1
584-
else do
585-
a2 <- VUM.unsafeRead m2 k
586-
when (x > a2) (VUM.unsafeWrite m2 k x)
587-
go (i + 1)
588-
go lo
589-
pure (m1, m2)
590-
591-
{- | Top two of the four candidates @{a1, a2, b1, b2}@ per group (each pair
592-
already ordered @m1 >= m2@, @-inf@ seeds included).
593-
-}
594-
mergeTop2 ::
595-
Int ->
596-
[(VUM.IOVector Double, VUM.IOVector Double)] ->
597-
IO (VUM.IOVector Double, VUM.IOVector Double)
598-
mergeTop2 nGroups parts = case parts of
599-
[] -> error "mergeTop2: no partials"
600-
((m10, m20) : rest) -> do
601-
let add (m1, m2) = do
602-
let go !k
603-
| k >= nGroups = pure ()
604-
| otherwise = do
605-
a1 <- VUM.unsafeRead m10 k
606-
a2 <- VUM.unsafeRead m20 k
607-
b1 <- VUM.unsafeRead m1 k
608-
b2 <- VUM.unsafeRead m2 k
609-
if b1 > a1
610-
then do
611-
VUM.unsafeWrite m10 k b1
612-
VUM.unsafeWrite m20 k (max a1 b2)
613-
else VUM.unsafeWrite m20 k (max a2 b1)
614-
go (k + 1)
615-
go 0
616-
mapM_ add rest
617-
pure (m10, m20)
618-
619-
finalizeTop2 ::
620-
Int -> VUM.IOVector Double -> VUM.IOVector Double -> IO (VU.Vector Double)
621-
finalizeTop2 nGroups m1 m2 = do
622-
out <- VUM.new nGroups
623-
let go !k
624-
| k >= nGroups = pure ()
625-
| otherwise = do
626-
a1 <- VUM.unsafeRead m1 k
627-
a2 <- VUM.unsafeRead m2 k
628-
let s = (if isInfinite a1 then 0 else a1) + (if isInfinite a2 then 0 else a2)
629-
VUM.unsafeWrite out k s
630-
go (k + 1)
631-
go 0
632-
VU.unsafeFreeze out
633-
634-
finalizeTop2Snd ::
635-
Int -> VUM.IOVector Double -> VUM.IOVector Double -> IO (VU.Vector Double)
636-
finalizeTop2Snd nGroups _m1 m2 = do
637-
out <- VUM.new nGroups
638-
let go !k
639-
| k >= nGroups = pure ()
640-
| otherwise = do
641-
a2 <- VUM.unsafeRead m2 k
642-
VUM.unsafeWrite out k (if isInfinite a2 then 0 / 0 else a2)
643-
go (k + 1)
644-
go 0
645-
VU.unsafeFreeze out
646-
647493
-------------------------------------------------------------------------------
648494
-- Integer mean (exact integer sum + count, divided once -> order-independent)
649495
-------------------------------------------------------------------------------

0 commit comments

Comments
 (0)