Skip to content

Add write benchmark of NYC Taxi Dataset. - #685

Open
Kuinox wants to merge 8 commits into
aloneguid:masterfrom
Kuinox:benchmark
Open

Add write benchmark of NYC Taxi Dataset.#685
Kuinox wants to merge 8 commits into
aloneguid:masterfrom
Kuinox:benchmark

Conversation

@Kuinox

@Kuinox Kuinox commented Dec 2, 2025

Copy link
Copy Markdown
Contributor

Since I want to do work to improve the performance of this library, I figured benching against real world data instead of synthetic data was needed.
This would avoid me writing optimisations for my specific use case and ruin performance for other scenarios.

BenchmarkDotNet v0.15.7, Windows 11 (10.0.26100.7171/24H2/2024Update/HudsonValley)
AMD Ryzen 9 7900X 4.70GHz, 1 CPU, 24 logical and 12 physical cores
.NET SDK 10.0.100
  [Host]   : .NET 8.0.22 (8.0.22, 8.0.2225.52707), X64 RyuJIT x86-64-v4
  ShortRun : .NET 8.0.22 (8.0.22, 8.0.2225.52707), X64 RyuJIT x86-64-v4

Job=ShortRun  IterationCount=3  LaunchCount=1
WarmupCount=3

| Method                         | Dataset        | Mean       | Error    | StdDev   | Allocated  |
|------------------------------- |--------------- |-----------:|---------:|---------:|-----------:|
| 'Parquet.Net -> MemoryStream'  | tripdata       |   466.8 ms | 383.5 ms | 21.02 ms |  727.13 MB |
| 'ParquetSharp -> MemoryStream' | tripdata       |   256.4 ms | 446.9 ms | 24.50 ms |   44.78 MB |
| 'Parquet.Net -> Disk'          | tripdata       |   453.2 ms | 369.3 ms | 20.24 ms |  697.19 MB |
| 'ParquetSharp -> Disk'         | tripdata       |   275.8 ms | 341.0 ms | 18.69 ms |   11.61 MB |
| 'Parquet.Net -> MemoryStream'  | tripdata-large | 1,144.2 ms | 981.2 ms | 53.78 ms | 2103.57 MB |
| 'ParquetSharp -> MemoryStream' | tripdata-large |   585.0 ms | 510.3 ms | 27.97 ms |  172.53 MB |
| 'Parquet.Net -> Disk'          | tripdata-large | 1,166.8 ms | 923.9 ms | 50.64 ms |  2007.4 MB |
| 'ParquetSharp -> Disk'         | tripdata-large |   591.0 ms | 213.6 ms | 11.71 ms |   38.24 MB |

Sadly the benchmark shows Parquet.Net is up to 2x slower than parquet sharp.

I see two way of improving the performance:

  • Parallelisation
  • Improving the encoding algos

Parallelisation

I believe we can run mosts of the compute intensive stuff in parallel. The users would then just do Task.WhenAll.

Improving the encoding algos

When I profiled for my use case, most of the time was spent on encoding, and a significant portion on deduplicating the input.
I believe there is room for improvement here and easily earn 10-20%.

@Kevin-Ross-ECC

Copy link
Copy Markdown

This is a really good benchmark and one that I'm going to start using in my own tuned version. I don't know how much I can share here as my version was written on company time and also has a slightly different API and drops support for anything lower than .NET9 but there is a lot of scope for performance improvements. Here are the results for the single dataset
image

@Kuinox

Kuinox commented Dec 2, 2025

Copy link
Copy Markdown
Contributor Author

Did both ran with the same runtime ?

@Kevin-Ross-ECC

Copy link
Copy Markdown

Yep same runtime (.NET10) on the same machine at the same time using a long BenchmarkDotNet job. Here is the full text output from the large dataset as well. You mention going parallel as one of your tuning paths, one thing I tried but then ended up going back on was the idea of parallel creation of the columns. The reason for that was that my usage case is to make a lot of parquet files (400+) on a machine with very few CPU cores and I found that by going parallel I ended up jamming up the threadpool so whilst a single benchmark would be a bit faster when used in anger with lots of files it slowed things down. TLDR, these gains are all through coding changes as opposed to going parallel


BenchmarkDotNet v0.15.8, Windows 11 (10.0.22631.6199/23H2/2023Update/SunValley3)
11th Gen Intel Core i7-1185G7 3.00GHz, 1 CPU, 8 logical and 4 physical cores
.NET SDK 10.0.100
  [Host]     : .NET 10.0.0 (10.0.0, 10.0.25.52411), X64 RyuJIT x86-64-v4
  DefaultJob : .NET 10.0.0 (10.0.0, 10.0.25.52411), X64 RyuJIT x86-64-v4


Method Mean Error StdDev Median
'Parquet.Net -> MemoryStream' 1,870.9 ms 86.49 ms 242.52 ms 1,811.7 ms
'Parquet.Net (Tuned) -> MemoryStream' 504.0 ms 24.82 ms 72.39 ms 475.7 ms

@aloneguid

Copy link
Copy Markdown
Owner

Yeah we know there is a lot of room for improvement. Especially since delta encoding was enabled by default, which is CPU heavy on write. This would be my focus for the near future.

The issue with these benchmarks is reliability. We don't know which compression is used, what encodings are utilised by each library, what's the file size, how efficient the target file is and so on. This is just testing defaults, which I can change for existing parquet.net and get at least 10x speed increase. It will be more useful to have benchmarks which test two libraries in exactly the same conditions. It's hard to achieve though.

@Kuinox

Kuinox commented Dec 2, 2025

Copy link
Copy Markdown
Contributor Author

Good point, i'll set the same compression algo for each bench

@Kevin-Ross-ECC

Copy link
Copy Markdown

Agreed performance tuning is a complex subject and it is very easy to "thumb the scales" to get a good looking benchmark that is not reflective of the real world. I haven't focused on benchmarking other libraries just comparing the stock ParquetDotNet (which is excellent BTW) against my modified version. I haven't changed any of the defaults or encoding options so both versions make identical files down to the bit in fact that is one of my tests to make sure I haven't broken anything!

@aloneguid

Copy link
Copy Markdown
Owner

I'd like to check what parquetsharp is using for integer compression as well, thanks for kicking this off. @Kevin-Ross-ECC is using some magic with SIMD. I haven't got as far as that (which I'm really excited about).

@aloneguid

Copy link
Copy Markdown
Owner

Just did a quick test, also this lib generates files that are much smaller (263b vs 57.8kb)

-a---          02/12/2025    10:12            263 sample-pqnet.parquet
-a---          02/12/2025    10:12          57855 sample-pqsharp.parquet

with identical physical compression (SNAPPY).

ParquetSharp will use RLE_DICTIONARY for integer column:

image

whereas this lib defaults to DELTA_BINARY_PACKED:

image

So we are comparing two different logical compression algorithms.

@Kuinox

Kuinox commented Dec 2, 2025

Copy link
Copy Markdown
Contributor Author

I added the logical compression algorithm as a bench parameter, sadly ParquetSharp is a bit uncooperative at outputing RleDictionary, so it will be tonight when I will have the time

BenchmarkDotNet v0.15.7, Windows 11 (10.0.26100.7171/24H2/2024Update/HudsonValley)
Intel Xeon E-2288G CPU 3.70GHz, 1 CPU, 8 logical and 8 physical cores
.NET SDK 10.0.100-preview.7.25380.108
  [Host]   : .NET 8.0.22 (8.0.22, 8.0.2225.52707), X64 RyuJIT x86-64-v3
  ShortRun : .NET 8.0.22 (8.0.22, 8.0.2225.52707), X64 RyuJIT x86-64-v3

Job=ShortRun  IterationCount=3  LaunchCount=1
WarmupCount=3

| Method                         | Dataset        | Encoding          | Mean     | Error     | StdDev   | Gen0      | Allocated |
|------------------------------- |--------------- |------------------ |---------:|----------:|---------:|----------:|----------:|
| 'Parquet.Net -> MemoryStream'  | tripdata       | Plain             | 192.4 ms |  23.56 ms |  1.29 ms |         - | 182.48 MB |
| 'ParquetSharp -> MemoryStream' | tripdata       | Plain             | 309.1 ms | 254.97 ms | 13.98 ms |         - | 117.29 MB |
| 'Parquet.Net -> Disk'          | tripdata       | Plain             | 192.8 ms |  26.13 ms |  1.43 ms |         - | 119.48 MB |
| 'ParquetSharp -> Disk'         | tripdata       | Plain             | 297.4 ms |  84.21 ms |  4.62 ms |         - |  24.58 MB |
| 'Parquet.Net -> MemoryStream'  | tripdata       | DeltaBinaryPacked | 226.7 ms |  54.93 ms |  3.01 ms | 1000.0000 | 144.84 MB |
| 'ParquetSharp -> MemoryStream' | tripdata       | DeltaBinaryPacked | 316.7 ms |  22.11 ms |  1.21 ms |         - |  91.67 MB |
| 'Parquet.Net -> Disk'          | tripdata       | DeltaBinaryPacked | 240.2 ms |  62.06 ms |  3.40 ms | 1000.0000 |  98.48 MB |
| 'ParquetSharp -> Disk'         | tripdata       | DeltaBinaryPacked | 315.7 ms |  67.99 ms |  3.73 ms |         - |  21.46 MB |
| 'Parquet.Net -> MemoryStream'  | tripdata-large | Plain             | 651.2 ms |  66.45 ms |  3.64 ms |         - | 747.49 MB |
| 'ParquetSharp -> MemoryStream' | tripdata-large | Plain             | 859.3 ms |  54.49 ms |  2.99 ms |         - | 264.32 MB |
| 'Parquet.Net -> Disk'          | tripdata-large | Plain             | 687.9 ms | 122.15 ms |  6.70 ms |         - | 544.17 MB |
| 'ParquetSharp -> Disk'         | tripdata-large | Plain             | 864.3 ms |  59.39 ms |  3.26 ms |         - |  78.72 MB |
| 'Parquet.Net -> MemoryStream'  | tripdata-large | DeltaBinaryPacked | 766.5 ms |  97.26 ms |  5.33 ms | 3000.0000 | 592.45 MB |
| 'ParquetSharp -> MemoryStream' | tripdata-large | DeltaBinaryPacked | 910.8 ms | 118.30 ms |  6.48 ms |         - |  241.1 MB |
| 'Parquet.Net -> Disk'          | tripdata-large | DeltaBinaryPacked | 780.3 ms |  39.55 ms |  2.17 ms | 3000.0000 | 418.01 MB |
| 'ParquetSharp -> Disk'         | tripdata-large | DeltaBinaryPacked | 926.8 ms | 164.79 ms |  9.03 ms |         - |  68.84 MB |

That's way better compared to ParquetSharp.
But the purpose of this benchmark was not to compare ParquetSharp, but Parquet.Net to itself (a version using the nuget package would allow to be a baseline when doing optimisations).

@aloneguid

Copy link
Copy Markdown
Owner

@Kuinox it is possible to baseline benchmarks of local codebase to a released version, see https://benchmarkdotnet.org/articles/samples/IntroNuGet.html

@Kuinox
Kuinox marked this pull request as draft December 4, 2025 13:38
@Kuinox

Kuinox commented Dec 4, 2025

Copy link
Copy Markdown
Contributor Author

@Kuinox it is possible to baseline benchmarks of local codebase to a released version, see https://benchmarkdotnet.org/articles/samples/IntroNuGet.html

Yes, but it looks like this specific endpoint is deprecated (from what I see in Parquet.Net warnings).
If you want I can fixes the warnings along with my PR.
BTW, this was an early draft, I think the bench comparison with ParquetSharp would be good to put on the Readme, so I'm working on two benches, one for the comparison with ParquetSharp, and one to compare Parquet.Net with it's past versions, as a less synthetic write bench.

@aloneguid

Copy link
Copy Markdown
Owner

That's good, I always wanted to build something like this for Parquet.Net, which measures perf on each commit to master (inspired by https://github.com/microsoft/Microsoft.IO.RecyclableMemoryStream?tab=readme-ov-file#benchmark-tests):

image

@aloneguid

Copy link
Copy Markdown
Owner

I don't know how it's going to work though without re-testing each commit at once. Action agents allocate random node with unpredictable performance.

@Kuinox

Kuinox commented Dec 4, 2025

Copy link
Copy Markdown
Contributor Author

Action agents allocate random node with unpredictable performance.

You or someone can host a github action runner on their machine.
Or we do a script to rebench ever commit and do it manually like you suggested.

I do have a NAS at home, and a bare metal server at ovh. They will have less noise, but there are still things running in background, and use old or low power CPUs (and sometimes I do run kind of heavy stuff on it).

@Kuinox
Kuinox marked this pull request as ready for review December 14, 2025 02:30
@Kuinox

Kuinox commented Dec 14, 2025

Copy link
Copy Markdown
Contributor Author

I have a good news, and bad news:
Good news: this PR should be ready, I refactored the bench project overall.
And, we are still fast on what I initially tested.

| Method                         | Dataset        | Schema | LogicalEncoding   | Mean       | Error       | StdDev    | Gen0        | Gen1      | Gen2      | Allocated  |
|------------------------------- |--------------- |------- |------------------ |-----------:|------------:|----------:|------------:|----------:|----------:|-----------:|
| 'Parquet.Net -> MemoryStream'  | tripdata-large | small  | Plain             |   487.0 ms |    17.88 ms |   0.98 ms |           - |         - |         - |  876.33 MB |
| 'ParquetSharp -> MemoryStream' | tripdata-large | small  | Plain             |   704.2 ms |    53.23 ms |   2.92 ms |           - |         - |         - |   81.34 MB |
| 'Parquet.Net -> MemoryStream'  | tripdata-large | small  | RleDictionary     | 1,578.5 ms | 1,661.14 ms |  91.05 ms |           - |         - |         - | 2177.58 MB |
| 'ParquetSharp -> MemoryStream' | tripdata-large | small  | RleDictionary     |   645.6 ms |    95.29 ms |   5.22 ms |           - |         - |         - |   38.27 MB |
| 'Parquet.Net -> MemoryStream'  | tripdata-large | small  | DeltaBinaryPacked |   485.0 ms |    68.00 ms |   3.73 ms |   4000.0000 |         - |         - |  531.83 MB |
| 'ParquetSharp -> MemoryStream' | tripdata-large | small  | DeltaBinaryPacked |   616.8 ms |    81.60 ms |   4.47 ms |           - |         - |         - |   63.62 MB |
| 'Parquet.Net -> MemoryStream'  | tripdata       | small  | Plain             |   134.7 ms |    10.81 ms |   0.59 ms |           - |         - |         - |  154.07 MB |
| 'ParquetSharp -> MemoryStream' | tripdata       | small  | Plain             |   222.6 ms |    87.87 ms |   4.82 ms |           - |         - |         - |   25.42 MB |
| 'Parquet.Net -> MemoryStream'  | tripdata       | small  | RleDictionary     |   380.9 ms |   188.42 ms |  10.33 ms |           - |         - |         - |  697.19 MB |
| 'ParquetSharp -> MemoryStream' | tripdata       | small  | RleDictionary     |   231.1 ms |    60.67 ms |   3.33 ms |           - |         - |         - |   11.63 MB |
| 'Parquet.Net -> MemoryStream'  | tripdata       | small  | DeltaBinaryPacked |   149.5 ms |    53.69 ms |   2.94 ms |   1250.0000 |         - |         - |   75.15 MB |
| 'ParquetSharp -> MemoryStream' | tripdata       | small  | DeltaBinaryPacked |   203.3 ms |    27.87 ms |   1.53 ms |           - |         - |         - |    19.8 MB |

Now the bad news:

  • It looks like that there is some metadata issue with RleDictionary setting, I didn't dug in details. We are also slower on it than ParquetSharp.
  • The quick bench I started with, tested a few columns, when testing with almost all the columns, ParquetSharp is now ahead on all the cases.
| Method                         | Dataset        | Schema | LogicalEncoding   | Mean       | Error       | StdDev    | Gen0        | Gen1      | Gen2      | Allocated  |
|------------------------------- |--------------- |------- |------------------ |-----------:|------------:|----------:|------------:|----------:|----------:|-----------:|
| 'Parquet.Net -> MemoryStream'  | tripdata       | full   | Plain             |   897.8 ms |   244.71 ms |  13.41 ms |  33000.0000 |         - |         - |  747.26 MB |
| 'ParquetSharp -> MemoryStream' | tripdata       | full   | Plain             |   815.9 ms |   134.18 ms |   7.35 ms |   1000.0000 |         - |         - |    97.6 MB |
| 'Parquet.Net -> MemoryStream'  | tripdata       | full   | RleDictionary     | 2,003.0 ms |   242.16 ms |  13.27 ms |  12000.0000 |         - |         - | 2346.94 MB |
| 'ParquetSharp -> MemoryStream' | tripdata       | full   | RleDictionary     |   901.4 ms |    76.55 ms |   4.20 ms |           - |         - |         - |   63.44 MB |
| 'Parquet.Net -> MemoryStream'  | tripdata       | full   | DeltaBinaryPacked |   825.1 ms |   241.01 ms |  13.21 ms |  35000.0000 |         - |         - |  659.35 MB |
| 'ParquetSharp -> MemoryStream' | tripdata       | full   | DeltaBinaryPacked |   780.5 ms |    72.07 ms |   3.95 ms |   1000.0000 |         - |         - |   87.38 MB |
| 'Parquet.Net -> MemoryStream'  | tripdata-large | full   | Plain             | 3,022.6 ms |   727.29 ms |  39.87 ms | 107000.0000 |         - |         - | 2998.97 MB |
| 'ParquetSharp -> MemoryStream' | tripdata-large | full   | Plain             | 2,584.2 ms | 1,144.11 ms |  62.71 ms |   5000.0000 |         - |         - |  311.61 MB |
| 'Parquet.Net -> MemoryStream'  | tripdata-large | full   | RleDictionary     | 6,833.1 ms |   911.24 ms |  49.95 ms |  38000.0000 | 1000.0000 | 1000.0000 | 6990.92 MB |
| 'ParquetSharp -> MemoryStream' | tripdata-large | full   | RleDictionary     | 2,543.5 ms |   160.55 ms |   8.80 ms |   1000.0000 |         - |         - |  203.98 MB |
| 'Parquet.Net -> MemoryStream'  | tripdata-large | full   | DeltaBinaryPacked | 3,125.1 ms |   103.51 ms |   5.67 ms | 114000.0000 | 1000.0000 |         - | 2556.26 MB |
| 'ParquetSharp -> MemoryStream' | tripdata-large | full   | DeltaBinaryPacked | 2,438.5 ms |   604.95 ms |  33.16 ms |   4000.0000 |         - |         - |  279.01 MB |

But that's works for another PR.

# Conflicts:
#	src/Parquet.PerfRunner/Parquet.PerfRunner.csproj
#	src/Parquet.PerfRunner/Program.cs
@Kuinox

Kuinox commented Dec 21, 2025

Copy link
Copy Markdown
Contributor Author
image @aloneguid I did some refactoring to handle safely parallelised writing, without changing the external API. The benchmark rely on this PR, but I can start another PR without the benchmark if you want

@Kuinox

Kuinox commented Dec 21, 2025

Copy link
Copy Markdown
Contributor Author
| Method          | Dataset        | LogicalEncoding   | ColumnName           | Mean        | Error     | StdDev    | Median      | Gen0       | Allocated |
|---------------- |--------------- |------------------ |--------------------- |------------:|----------:|----------:|------------:|-----------:|----------:|
| ParquetNetAsync | tripdata-large | Plain             | Airport_fee          |    54.69 ms |  1.075 ms |  1.104 ms |    54.43 ms |          - | 132.94 MB |
| ParquetNetAsync | tripdata-large | Plain             | congestion_surcharge |    60.63 ms |  0.840 ms |  0.786 ms |    60.75 ms |          - | 135.38 MB |
| ParquetNetAsync | tripdata-large | Plain             | DOLocationID         |    82.30 ms |  0.710 ms |  0.630 ms |    82.42 ms |          - |  75.55 MB |
| ParquetNetAsync | tripdata-large | Plain             | extra                |    83.17 ms |  0.927 ms |  0.821 ms |    83.15 ms |          - | 155.31 MB |
| ParquetNetAsync | tripdata-large | Plain             | fare_amount          |   146.61 ms |  1.745 ms |  1.633 ms |   146.92 ms |          - | 201.67 MB |
| ParquetNetAsync | tripdata-large | Plain             | impro(...)harge [21] |    47.05 ms |  0.728 ms |  0.681 ms |    47.00 ms |          - | 141.27 MB |
| ParquetNetAsync | tripdata-large | Plain             | mta_tax              |    50.59 ms |  0.796 ms |  0.782 ms |    50.61 ms |          - | 142.18 MB |
| ParquetNetAsync | tripdata-large | Plain             | passenger_count      |    71.85 ms |  0.867 ms |  0.811 ms |    71.81 ms |          - | 143.08 MB |
| ParquetNetAsync | tripdata-large | Plain             | payment_type         |    69.14 ms |  1.350 ms |  1.555 ms |    69.18 ms |          - | 151.54 MB |
| ParquetNetAsync | tripdata-large | Plain             | PULocationID         |    76.99 ms |  0.762 ms |  0.713 ms |    76.92 ms |          - |  74.22 MB |
| ParquetNetAsync | tripdata-large | Plain             | RatecodeID           |    49.22 ms |  0.480 ms |  0.426 ms |    49.15 ms |          - |  132.4 MB |
| ParquetNetAsync | tripdata-large | Plain             | store_and_fwd_flag   |   155.00 ms |  2.124 ms |  1.883 ms |   154.09 ms | 33500.0000 |  301.4 MB |
| ParquetNetAsync | tripdata-large | Plain             | tip_amount           |   146.68 ms |  1.884 ms |  1.762 ms |   146.30 ms |          - |  198.8 MB |
| ParquetNetAsync | tripdata-large | Plain             | tolls_amount         |    53.86 ms |  0.467 ms |  0.390 ms |    53.91 ms |          - | 145.05 MB |
| ParquetNetAsync | tripdata-large | Plain             | total_amount         |   170.72 ms |  1.660 ms |  1.552 ms |   170.94 ms |          - | 227.69 MB |
| ParquetNetAsync | tripdata-large | Plain             | tpep_(...)etime [21] |   648.34 ms | 12.309 ms | 12.640 ms |   640.27 ms | 36000.0000 | 567.94 MB |
| ParquetNetAsync | tripdata-large | Plain             | tpep_pickup_datetime |   694.20 ms |  8.190 ms |  7.260 ms |   691.56 ms | 36000.0000 | 563.58 MB |
| ParquetNetAsync | tripdata-large | Plain             | trip_distance        |   153.96 ms |  1.940 ms |  1.815 ms |   153.68 ms |          - | 220.39 MB |
| ParquetNetAsync | tripdata-large | Plain             | VendorID             |    52.48 ms |  0.172 ms |  0.153 ms |    52.46 ms |          - |  40.32 MB |

I'm digging why when we add columns we are suddendly slower than ParquetSharp.
It looks like the culprit is the DateTime.
image

@Kuinox

Kuinox commented Dec 21, 2025

Copy link
Copy Markdown
Contributor Author

The causes is that, on the read end, we assume that DateTime are local if unspecified, and on the write end, we always write utc DateTime.

image

You can see that on the taxi dataset, without specifying any kind of DateTime, it's substracting my UTC offset of the original DateTime, so I think that's a bug.
It was caught in a post merge review, but was forgotten.

It's too late to dig deeper, but disabling the useless convertion here double the speed, I'll make a proper patch later.

@aloneguid

Copy link
Copy Markdown
Owner

V6 preview (in master) had a lot of improvements, I'd recommend looking into it.

@Kuinox

Kuinox commented May 5, 2026

Copy link
Copy Markdown
Contributor Author

@aloneguid On another project I have some benchmark and I see 50% perf regression for plain encoding (except bool).
Also it looks like I can't force Parquet.Net on Dictionnary encoding on V6 preview.

btw the other project in question is a .NET Parquet lib :p

@aloneguid

Copy link
Copy Markdown
Owner

@Kuinox that's interesting, because all the tests I came up so far show massive performance win v5 vs v6. I'd be interested to see how you benchmark.
P.S. You can force dictionary encoding per column in ParquetOptions.

@Kuinox

Kuinox commented May 5, 2026

Copy link
Copy Markdown
Contributor Author

this doesn't produce a parquet that use dictionary encoding:

#:package Parquet.Net@6.0.0-pre.8

using Parquet;
using Parquet.Schema;

var field = new DataField<int>("value");
var schema = new Parquet.Schema.ParquetSchema(field);
var options = new ParquetOptions
{
    CompressionMethod = CompressionMethod.None,
    DictionaryEncodingThreshold = 1.0,
    DictionaryEncodingSampleSize = 0
};
options.ColumnEncodingHints.Add(field.Path.ToString(), EncodingHint.Dictionary);

var values = Enumerable.Range(0, 4_096).Select(static i => i % 16).ToArray();

await using var stream = File.Create("parquetnet-int32-dictionary-hint.parquet");
await using var writer = await Parquet.ParquetWriter.CreateAsync(schema, stream, options, false);
using var rowGroup = writer.CreateRowGroup();
await rowGroup.WriteAsync<int>(field, values.AsMemory(), null, null, default);

@Kuinox

Kuinox commented May 5, 2026

Copy link
Copy Markdown
Contributor Author

Huh, I asked codex to update & run benchs and it decided to puch the branch by itself 🤡

@Kuinox

Kuinox commented May 5, 2026

Copy link
Copy Markdown
Contributor Author

The self comparison bench added by codex in this commit have the same result that what I found:

Method Job Arguments Dataset LogicalEncoding Mean Error StdDev Gen0 Gen1 Gen2 Allocated
'Parquet.Net source' Parquet.Net (local) Default tripdata Plain 1,039.3 ms 412.2 ms 22.59 ms 33000.0000 - - 1.76 GB
'Parquet.Net source' Parquet.Net 5.6.0 /p:ParquetVersion=5.6.0 tripdata Plain 891.5 ms 455.5 ms 24.97 ms 33000.0000 - - 1.76 GB
'Parquet.Net source' Parquet.Net (local) Default tripdata RleDictionary 1,006.4 ms 390.5 ms 21.40 ms 22000.0000 - - 1.77 GB
'Parquet.Net source' Parquet.Net 5.6.0 /p:ParquetVersion=5.6.0 tripdata RleDictionary 1,793.1 ms 370.4 ms 20.31 ms 12000.0000 - - 2.39 GB
'Parquet.Net source' Parquet.Net (local) Default tripdata DeltaBinaryPacked 927.2 ms 569.7 ms 31.23 ms 35000.0000 - - 1.07 GB
'Parquet.Net source' Parquet.Net 5.6.0 /p:ParquetVersion=5.6.0 tripdata DeltaBinaryPacked 768.4 ms 416.3 ms 22.82 ms 35000.0000 - - 1.07 GB
'Parquet.Net source' Parquet.Net (local) Default tripdata-large Plain 3,724.5 ms 3,682.9 ms 201.87 ms 108000.0000 2000.0000 1000.0000 7.23 GB
'Parquet.Net source' Parquet.Net 5.6.0 /p:ParquetVersion=5.6.0 tripdata-large Plain 3,208.4 ms 1,615.8 ms 88.57 ms 107000.0000 1000.0000 - 6.17 GB
'Parquet.Net source' Parquet.Net (local) Default tripdata-large RleDictionary 3,779.6 ms 451.4 ms 24.74 ms 73000.0000 - - 7.24 GB
'Parquet.Net source' Parquet.Net 5.6.0 /p:ParquetVersion=5.6.0 tripdata-large RleDictionary 6,035.2 ms 5,437.7 ms 298.06 ms 37000.0000 - - 7.63 GB
'Parquet.Net source' Parquet.Net (local) Default tripdata-large DeltaBinaryPacked 3,001.3 ms 3,546.4 ms 194.39 ms 114000.0000 1000.0000 - 3.87 GB
'Parquet.Net source' Parquet.Net 5.6.0 /p:ParquetVersion=5.6.0 tripdata-large DeltaBinaryPacked 2,686.9 ms 1,473.0 ms 80.74 ms 114000.0000 1000.0000 - 4.08 GB

@aloneguid

Copy link
Copy Markdown
Owner

API in v6 is not compatible with v5, so I'm not quite sure how LLM made those changes work.

@Kuinox

Kuinox commented May 5, 2026

Copy link
Copy Markdown
Contributor Author

It sprinkled with #if a bit everywhere, and it very ugly.

I can make it better, but first, do you even want to support benching against the previous version ?

@aloneguid

Copy link
Copy Markdown
Owner

Not really, only wondering why your tests are slower on v6. My approach was to run benchmarks on v5 branch, then on master. Separately one after another on the same machine.

@Kuinox

Kuinox commented May 5, 2026

Copy link
Copy Markdown
Contributor Author

Ok, do you want I keep digging into a more minimal benchmark/dataset to compare the two ?

@aloneguid

Copy link
Copy Markdown
Owner

Not at all, I was just wondering how 5 is slower, I couldn't reproduce this with a test.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants