Skip to content

bench(parquet): measure ArrowWriter memory allocation - #11268

Merged
etseidl merged 1 commit into
apache:mainfrom
HippoBaro:mem_bench
Sep 28, 2026
Merged

etseidl merged 1 commit into
apache:mainfrom
HippoBaro:mem_bench

Conversation

@HippoBaro

@HippoBaro HippoBaro commented Sep 28, 2026 •

Copy link
Copy Markdown
Contributor

Which issue does this PR close?

Rationale for this change

We do not track memory usage for the Parquet writer. Native REE support should improve memory usage materially so we need some bench harnesses to keep track of that over time.

Add a dedicated memory benchmark with two complementary measurements: incremental peak live memory and cumulative allocated bytes over a complete writer lifecycle.

What changes are included in this PR?

  • Add the arrow_writer_peak_memory Criterion benchmark with peak-live-memory and cumulative-allocation measurements.
  • Measure writer construction, write(), and close(), including writer destruction.
  • Exclude input construction and configuration cloning from the measured window.
  • Exercise primitive, sparse, dictionary, run-end-encoded, and nested inputs under the default, Parquet 2.0, and content-defined-chunking configurations.
  • Write to a discarding sink so output retention outside the writer does not dominate the measurements.
  • Keep allocation tracking local to the benchmark through a thread-local allocator wrapper.

The measurements target the synchronous, single-threaded writer; they are not whole-process resident-memory measurements. How memory usage is tracked is the same as the existing mechanism added in #9846.

Are these changes tested?

NA

Are there any user-facing changes?

NA

Add a dedicated ArrowWriter memory benchmark to complement the timing
suite with two distinct views of allocation behavior: incremental peak
live memory and cumulative allocated bytes over each writer lifecycle.

Measure writer construction, `write()`, and `close()`, including writer
destruction, while excluding input construction and configuration cloning.
Exercise a focused matrix of primitive, sparse, dictionary,
run-end-encoded, and nested inputs across the default, Parquet 2.0, and
content-defined chunking configurations.

Keep tracking local to the benchmark through a thread-local allocator
wrapper, so production allocator behavior remains unchanged.

Signed-off-by: Hippolyte Barraud <hippolyte.barraud@datadoghq.com>
@github-actions github-actions Bot added the parquet Changes to the parquet crate label Sep 28, 2026
@HippoBaro

HippoBaro commented Sep 28, 2026 •

Copy link
Copy Markdown
Contributor Author

For reference, as of this PR, here are the results (all units MiB):

+----------------------------------------+-----------------------------+-----------------------------+
|                                        |      Peak live memory       |    Cumulative allocated     |
| Workload                               | default |parquet_2|   cdc   | default |parquet_2|   cdc   |
+----------------------------------------+---------+---------+---------+---------+---------+---------+
| primitive_non_null                     |   55.270|   57.803|   55.876|  334.382|  277.752|  397.917|
| primitive_nullable                     |   47.373|   51.496|   48.569|  302.016|  263.216|  356.726|
| primitive_sparse_99pct_null            |    4.823|    9.827|    5.653|   21.340|   26.343|   35.923|
| bool_non_null                          |    9.205|    9.221|   12.696|    9.350|    9.990|   17.352|
| string_non_null                        |  417.971|  419.217|  417.802| 1546.989|  836.751| 1611.486|
| short_string_non_null                  |   20.771|   15.094|   21.545|   52.736|   24.213|   61.697|
| byte_array_sparse_99pct_null           |    9.053|   17.124|   10.003|   27.040|   31.702|   35.191|
| fixed_size_binary_non_null             |   72.513|   75.670|   26.975|  123.222|  208.227|  129.152|
| fixed_size_binary_sparse               |   58.297|   61.850|   25.287|   83.759|  182.615|   93.967|
| decimal128_non_null                    |   72.513|   62.516|   26.628|  123.222|  146.324|  129.152|
| decimal128_sparse                      |   58.297|   55.473|   24.842|   83.759|  136.176|   93.967|
| int64_dictionary                       |   24.595|   25.571|   17.754|   55.582|   45.906|   63.406|
| int64_dictionary_1pct_cardinality      |   19.899|   20.900|   13.231|   28.201|   29.202|   36.251|
| string_dictionary                      |  165.524|  166.396|  165.810|  595.177|  320.828|  608.167|
| string_dictionary_1pct_cardinality     |   12.306|   14.308|   12.974|   15.014|   17.015|   23.063|
| decimal128_dictionary                  |   74.399|   78.148|   31.028|   99.996|  208.382|  108.573|
| decimal128_dictionary_1pct_cardinality |   74.298|   66.277|   28.195|   99.761|   69.375|  108.335|
| bool_dictionary                        |   14.136|   14.136|   14.760|   15.775|   16.409|   24.977|
| string_ree_low_cardinality             |   23.255|   25.257|   23.884|   27.905|   29.907|   35.947|
| int64_ree_low_cardinality              |   16.463|   17.465|   17.272|   28.968|   29.970|   36.979|
| fixed_size_binary_ree_low_cardinality  |   88.447|   74.327|   44.578|  144.603|   78.973|  154.108|
| string_ree_of_dict                     |   12.491|   14.493|   13.989|   17.143|   19.145|   25.590|
| numeric_ree_of_dict                    |   20.695|   21.697|   15.522|   33.208|   34.210|   41.656|
| ree_struct_of_dict                     |   28.566|   31.569|   30.064|   51.527|   54.531|   69.821|
| int32_ree_99pct_null                   |    8.207|    9.207|    8.207|   10.423|   11.424|   12.506|
| string_ree_99pct_null                  |    8.406|   10.408|    8.406|   10.588|   12.590|   12.670|
| struct_ree                             |   44.424|   47.428|   45.427|   78.491|   81.495|   96.508|
| list_int32_ree                         |  122.342|  123.340|  122.479|  175.730|  176.726|  253.847|
| list_of_int32_ree                      |   66.709|   67.709|   68.187|  100.423|  101.422|  144.439|
| struct_of_list_of_int32_ree_fragmented |   66.710|   67.710|   68.188|  124.428|  125.426|  168.443|
| list_of_list_int32_ree                 |  122.384|  123.382|  122.524|  185.822|  186.817|  263.939|
| ree_list_ree_list                      |  124.083|  125.084|  124.083|  241.635|  242.630|  319.709|
+----------------------------------------+---------+---------+---------+---------+---------+---------+

@HippoBaro HippoBaro changed the title bench(parquet): measure ArrowWriter memory allocation bench(parquet): measure ArrowWriter memory allocation Sep 28, 2026

@etseidl etseidl left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

LGTM! Thanks @HippoBaro.

@etseidl
etseidl merged commit 74b630b into apache:main Sep 28, 2026
32 checks passed
@Jefffrey Jefffrey added the development-process Internal changes; PRs with this label are excluded from changelog label Sep 29, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

development-process Internal changes; PRs with this label are excluded from changelog parquet Changes to the parquet crate

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants