Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
85c6e4a
WIP: Initial sketch of the parquet writer design
sharmrj Aug 5, 2026
89d7b6d
WIP Parquet Writer (does not compile)
sharmrj Aug 18, 2026
e8c2b85
Implemented HasBuffer instance for the file backed buffer; Cleaned up…
sharmrj Aug 19, 2026
c3fc6c3
Tests for `HasBuffer` instances
sharmrj Aug 19, 2026
f292f51
Changes to make Writer.hs compile so we can run tests
sharmrj Aug 19, 2026
00f105b
fix conflicts with main
sharmrj Aug 19, 2026
167ab08
Fixed ensureCapacity so that it works correctly with pinned ByteArray…
sharmrj Aug 19, 2026
6fd2e80
implement a cleaned up Parquet Writer
sharmrj Aug 19, 2026
f76f518
added some comments
sharmrj Aug 19, 2026
ad47e3b
added benchmarks and a stress test
sharmrj Aug 19, 2026
237a139
Optimized hotspots
sharmrj Aug 19, 2026
995fc7b
ran fourmolu
sharmrj Aug 19, 2026
74ff879
hlint
sharmrj Aug 19, 2026
ddab22c
fourmolu again
sharmrj Aug 19, 2026
bb46712
parquet.thrift was committed by accident
sharmrj Aug 19, 2026
db19108
made sure test parquet files can be recognized in CI.
sharmrj Aug 19, 2026
31b054a
Changed dataframe-parquet.cabal after the recommendations from CI's c…
sharmrj Aug 19, 2026
fa0aeee
removed ghcoptions from dataframe-parquet-10gb stress so CI wont reje…
sharmrj Aug 20, 2026
0479679
Removed the comment at the beginning of Writer.hs
sharmrj Aug 20, 2026
c203983
Fixed a bug where attempting to write a parquet file with a name that…
sharmrj Aug 21, 2026
8e067e1
Fixed a bug where the compressed size was being written to RowGroup.t…
sharmrj Aug 21, 2026
ab6f785
Removed some dead code; Fixed an issue where defLevels weren't counte…
sharmrj Aug 21, 2026
c486e02
Writer Refactor WIP
sharmrj Aug 23, 2026
bf8f486
removed dead code (refactor WIP)
sharmrj Aug 23, 2026
c38f2fa
Refactored the Parquet Writer for readability
sharmrj Aug 23, 2026
af73988
Merge remote-tracking branch 'refs/remotes/upstream/main' into parque…
sharmrj Aug 23, 2026
09e0ce0
Fixed a compile issue after pulling from main
sharmrj Aug 23, 2026
bddd38e
Reverted changes to the stress test
sharmrj Aug 23, 2026
80dd2f3
Fixed a `Bitmap` import
sharmrj Aug 23, 2026
4d37dc0
`writeByteStringToFile` no longer instantiates a new buffer.
sharmrj Aug 24, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 49 additions & 0 deletions dataframe-parquet/benchmark/Writer10GB.hs
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
module Main (main) where

import Control.DeepSeq (NFData (rnf))
import Criterion.Main (bench, defaultMain, envWithCleanup, whnfIO)
import DataFrame.IO.Parquet.Writer (writeParquet)
import DataFrame.Internal.DataFrame (DataFrame, forceDataFrame)
import DataFrame10GB (stressDataFrame)
import System.Directory (removeDirectoryRecursive)
import System.FilePath ((</>))
import System.IO.Temp (createTempDirectory, getCanonicalTemporaryDirectory)

data BenchmarkEnvironment = BenchmarkEnvironment
{ benchmarkDataFrame :: DataFrame
, benchmarkDirectory :: FilePath
, benchmarkOutput :: FilePath
}

instance NFData BenchmarkEnvironment where
rnf environment =
forceDataFrame (benchmarkDataFrame environment) `seq`
rnf (benchmarkDirectory environment) `seq`
rnf (benchmarkOutput environment)

prepareEnvironment :: IO BenchmarkEnvironment
prepareEnvironment = do
temporary <- getCanonicalTemporaryDirectory
directory <- createTempDirectory temporary "dataframe-parquet-writer-10gb"
pure
BenchmarkEnvironment
{ benchmarkDataFrame = stressDataFrame
, benchmarkDirectory = directory
, benchmarkOutput = directory </> "benchmark.parquet"
}

cleanupEnvironment :: BenchmarkEnvironment -> IO ()
cleanupEnvironment = removeDirectoryRecursive . benchmarkDirectory

main :: IO ()
main =
defaultMain
[ envWithCleanup prepareEnvironment cleanupEnvironment $ \environment ->
-- Memory usage for this benchmark will be north of 20 GB.
bench "write 10 GiB dataframe" $
whnfIO

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will this evaluate it enough that this benchmark is meaningful?

@sharmrj sharmrj Aug 20, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think whnfIO does enforce the IO action as it's just (). But I ran it withnfIO anyway just to make sure and got a similar result:

benchmarking write 10 GiB dataframe
time                 23.10 s    (22.94 s .. 23.30 s)
                     1.000 R²   (1.000 R² .. 1.000 R²)
mean                 23.25 s    (23.19 s .. 23.37 s)
std dev              107.8 ms   (22.15 ms .. 142.2 ms)
variance introduced by outliers: 19% (moderately inflated)

( writeParquet
(benchmarkOutput environment)
(benchmarkDataFrame environment)
)
]
62 changes: 62 additions & 0 deletions dataframe-parquet/dataframe-parquet.cabal
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ maintainer: mschavinda@gmail.com
copyright: (c) 2024-2026 Michael Chavinda
category: Data
tested-with: GHC ==9.4.8 || ==9.6.7 || ==9.8.4 || ==9.10.3 || ==9.12.2
extra-source-files: tests/data/*.parquet

common warnings
ghc-options:
Expand All @@ -28,6 +29,11 @@ common warnings
-Wunused-local-binds
-Wunused-packages

flag stress-tests
description: Build and run the opt-in 10 GiB Parquet roundtrip stress test.
default: False
manual: True

library
import: warnings
ghc-options: -O2
Expand All @@ -44,6 +50,11 @@ library
DataFrame.IO.Parquet.Thrift
DataFrame.IO.Parquet.Time
DataFrame.IO.Parquet.Utils
DataFrame.IO.Parquet.Writer
DataFrame.IO.Parquet.Writer.DefLevels
DataFrame.IO.Parquet.Writer.Encoder
DataFrame.IO.Parquet.Writer.Metadata
DataFrame.IO.Parquet.Writer.Options
DataFrame.IO.Utils.RandomAccess
DataFrame.Typed.IO.Parquet
build-depends: base >= 4 && < 5,
Expand All @@ -52,6 +63,7 @@ library
dataframe-core >= 2.4.1 && < 2.5,
dataframe-operations >= 2.4 && < 2.5,
dataframe-parsing >= 2.2 && < 2.3,
primitive >= 0.7 && < 0.11,
directory >= 1.3.0.0 && < 2,
filepath >= 1.4 && < 2,
Glob >= 0.10 && < 1,
Expand All @@ -64,3 +76,53 @@ library
zstd >= 0.1.2.0 && < 0.3
hs-source-dirs: src
default-language: Haskell2010


test-suite dataframe-parquet-tests
import: warnings
type: exitcode-stdio-1.0
main-is: Main.hs
hs-source-dirs: tests
build-depends: base >= 4 && < 5,
bytestring >= 0.11 && < 0.14,
dataframe-parquet,
filepath >= 1.4 && < 2,
temporary >= 1.3 && < 1.5,
HUnit >= 1.6 && < 1.8
default-language: Haskell2010

executable dataframe-parquet-10gb-stress
import: warnings
main-is: StressMain.hs
other-modules: DataFrame10GB
hs-source-dirs: stress
build-depends: base >= 4 && < 5,
dataframe-core >= 2.4 && < 2.5,
dataframe-parquet,
filepath >= 1.4 && < 2,
temporary >= 1.3 && < 1.5,
text >= 2.1 && < 3,
time >= 1.12 && < 2,
vector >= 0.13 && < 0.15
default-language: Haskell2010
-- ghc-options: -O2 -threaded -rtsopts -with-rtsopts=-N

benchmark dataframe-parquet-writer-10gb
import: warnings
type: exitcode-stdio-1.0
main-is: Writer10GB.hs
other-modules: DataFrame10GB
hs-source-dirs: benchmark, stress
build-depends: base >= 4 && < 5,
criterion >= 1 && < 2,
deepseq >= 1.4 && < 2,
dataframe-core >= 2.4 && < 2.5,
dataframe-parquet,
directory >= 1.3 && < 2,
filepath >= 1.4 && < 2,
temporary >= 1.3 && < 1.5,
text >= 2.1 && < 3,
time >= 1.12 && < 2,
vector >= 0.13 && < 0.15
default-language: Haskell2010
ghc-options: -O2 -threaded -rtsopts -with-rtsopts=-N
Loading
Loading