Skip to content

Introduce count_min_sketch::serialize_to#509

Open
cv4g wants to merge 5 commits into
apache:masterfrom
cv4g:countmin-serialize-to
Open

Introduce count_min_sketch::serialize_to#509
cv4g wants to merge 5 commits into
apache:masterfrom
cv4g:countmin-serialize-to

Conversation

@cv4g

@cv4g cv4g commented Jul 10, 2026

Copy link
Copy Markdown
Contributor

Hi,

thank you so much for considering the other two pull requests. I would like to propose one more change to count_min_sketch focussing on the serialization.
Currently there is a std::vector and a std::ostream& based API. ClickHouse is calling serialize() to obtain a vector and is then copying it's contents into another buffer that is then written to disk.
I would now like to remove this additional copy. The ClickHouse project is trying to avoid using standard library stream mechanisms which is why I would like to propose to add a sink-based API as an additional option.
Would you be okay with adding this? An advantage is that it could also be used internally to back both serialize() overloads and the std::ostream& overload is benefiting from the change as well.

The change set consists of four commits:

Baseline:   6029803 Add micro-benchmark for count_min_sketch serialization
            4e66c11 Add serialize_to sink API for count-min sketch
Sink API:   764cdf1 Extend micro-benchmark for count_min_sketch serialization
Final:      63e7f75 Serialize count_min_sketch sketch array in a single write call

4e66c11 introduces a new member function serialize_to() and updates both serialize() and serialize(std::ostream&) to call it. 63e7f75 improves the implementation of serialize_to() such that it is passing all sketch cells as a consecutive memory buffer to the sink instead of each one individually.

I've added a microbenchmark once more to ensure it's not pessimising the existing API:

6029803:

Run on (16 X 2000 MHz CPU s)
  CPU Caches:
    L1 Data 64 KiB (x16)
    L1 Instruction 64 KiB (x16)
    L2 Unified 2048 KiB (x16)
    L3 Unified 36864 KiB (x1)
  Load Average: 0.02, 0.07, 0.11
  --------------------------------------------------------------------------------------------------
  Benchmark                                        Time             CPU   Iterations UserCounters...
  --------------------------------------------------------------------------------------------------
  BM_CountMinGetSerializedSizeBytes/1024       0.482 ns        0.482 ns   1000000000 items_per_second=2.0746G/s
  BM_CountMinGetSerializedSizeBytes/4096       0.482 ns        0.482 ns   1000000000 items_per_second=2.07432G/s
  BM_CountMinGetSerializedSizeBytes/32768      0.482 ns        0.482 ns   1000000000 items_per_second=2.0739G/s
  BM_CountMinGetSerializedSizeBytes/65536      0.482 ns        0.482 ns   1000000000 items_per_second=2.07387G/s
  BM_CountMinSerializeVector/1024               1111 ns         1111 ns       628886 bytes_per_second=48.0814G/s
  BM_CountMinSerializeVector/4096               4680 ns         4679 ns       149599 bytes_per_second=45.6596G/s
  BM_CountMinSerializeVector/32768             52258 ns        52250 ns        12647 bytes_per_second=32.7081G/s
  BM_CountMinSerializeVector/65536            119323 ns       119305 ns         5739 bytes_per_second=28.6493G/s
  BM_CountMinSerializeOStream/1024             60520 ns        60512 ns        11637 bytes_per_second=904.132M/s
  BM_CountMinSerializeOStream/4096            242561 ns       242528 ns         2889 bytes_per_second=902.054M/s
  BM_CountMinSerializeOStream/32768          1963452 ns      1963189 ns          356 bytes_per_second=891.418M/s
  BM_CountMinSerializeOStream/65536          3928092 ns      3927308 ns          178 bytes_per_second=891.202M/s

764cdf1:

  Load Average: 0.12, 0.09, 0.12
  --------------------------------------------------------------------------------------------------
  Benchmark                                        Time             CPU   Iterations UserCounters...
  --------------------------------------------------------------------------------------------------
  BM_CountMinGetSerializedSizeBytes/1024       0.482 ns        0.482 ns   1000000000 items_per_second=2.07411G/s
  BM_CountMinGetSerializedSizeBytes/4096       0.482 ns        0.482 ns   1000000000 items_per_second=2.07378G/s
  BM_CountMinGetSerializedSizeBytes/32768      0.482 ns        0.482 ns   1000000000 items_per_second=2.07327G/s
  BM_CountMinGetSerializedSizeBytes/65536      0.482 ns        0.482 ns   1000000000 items_per_second=2.0743G/s
  BM_CountMinSerializeVector/1024              15813 ns        15811 ns        44268 bytes_per_second=3.3792G/s
  BM_CountMinSerializeVector/4096              63290 ns        63286 ns        11060 bytes_per_second=3.37585G/s
  BM_CountMinSerializeVector/32768            507039 ns       506968 ns         1376 bytes_per_second=3.37104G/s
  BM_CountMinSerializeVector/65536           1025428 ns      1025387 ns          678 bytes_per_second=3.33337G/s
  BM_CountMinSerializeOStream/1024             56433 ns        56425 ns        12404 bytes_per_second=969.608M/s
  BM_CountMinSerializeOStream/4096            228901 ns       228888 ns         3059 bytes_per_second=955.807M/s
  BM_CountMinSerializeOStream/32768          1863173 ns      1863021 ns          375 bytes_per_second=939.347M/s
  BM_CountMinSerializeOStream/65536          3676834 ns      3676281 ns          188 bytes_per_second=952.055M/s
  BM_CountMinSerializeToSink/1024               1272 ns         1272 ns       550463 bytes_per_second=42.0145G/s
  BM_CountMinSerializeToSink/4096               5159 ns         5158 ns       135711 bytes_per_second=41.4186G/s
  BM_CountMinSerializeToSink/32768             43027 ns        43021 ns        16200 bytes_per_second=39.7252G/s
  BM_CountMinSerializeToSink/65536             86007 ns        85981 ns         8089 bytes_per_second=39.753G/s

63e7f75:

Load Average: 0.31, 0.16, 0.14
  --------------------------------------------------------------------------------------------------
  Benchmark                                        Time             CPU   Iterations UserCounters...
  --------------------------------------------------------------------------------------------------
  BM_CountMinGetSerializedSizeBytes/1024       0.482 ns        0.482 ns   1000000000 items_per_second=2.07442G/s
  BM_CountMinGetSerializedSizeBytes/4096       0.482 ns        0.482 ns   1000000000 items_per_second=2.07343G/s
  BM_CountMinGetSerializedSizeBytes/32768      0.482 ns        0.482 ns   1000000000 items_per_second=2.07396G/s
  BM_CountMinGetSerializedSizeBytes/65536      0.482 ns        0.482 ns   1000000000 items_per_second=2.07333G/s
  BM_CountMinSerializeVector/1024               1115 ns         1115 ns       627606 bytes_per_second=47.9082G/s
  BM_CountMinSerializeVector/4096               4614 ns         4613 ns       151465 bytes_per_second=46.3141G/s
  BM_CountMinSerializeVector/32768             50043 ns        50036 ns        13952 bytes_per_second=34.1558G/s
  BM_CountMinSerializeVector/65536            114727 ns       114710 ns         5913 bytes_per_second=29.7968G/s
  BM_CountMinSerializeOStream/1024               809 ns          809 ns       867109 bytes_per_second=66.0104G/s
  BM_CountMinSerializeOStream/4096              3292 ns         3292 ns       212692 bytes_per_second=64.9082G/s
  BM_CountMinSerializeOStream/32768            32502 ns        32501 ns        21627 bytes_per_second=52.5838G/s
  BM_CountMinSerializeOStream/65536            76414 ns        76400 ns         9149 bytes_per_second=44.738G/s
  BM_CountMinSerializeToSink/1024                710 ns          710 ns       986351 bytes_per_second=75.2844G/s
  BM_CountMinSerializeToSink/4096               3189 ns         3189 ns       219511 bytes_per_second=66.9917G/s
  BM_CountMinSerializeToSink/32768             32720 ns        32715 ns        21305 bytes_per_second=52.2388G/s
  BM_CountMinSerializeToSink/65536             75691 ns        75675 ns         9093 bytes_per_second=45.1667G/s

Summary:

  serialize() vector

   Buckets     Baseline      Sink API        Final    Final vs Baseline
  ━━━━━━━━━  ━━━━━━━━━━━  ━━━━━━━━━━━━  ━━━━━━━━━━━  ━━━━━━━━━━━━━━━━━━━
      1024      1111 ns      15811 ns      1115 ns                1.00x
  ─────────  ───────────  ────────────  ───────────  ───────────────────
      4096      4679 ns      63286 ns      4613 ns                1.01x
  ─────────  ───────────  ────────────  ───────────  ───────────────────
     32768     52250 ns     506968 ns     50036 ns                1.04x
  ─────────  ───────────  ────────────  ───────────  ───────────────────
     65536    119305 ns    1025387 ns    114710 ns                1.04x

The vector serialization regressed badly in 764cdf1 but recovered in 63e7f75 when all sketch cells are written in a single call. It seems that the compiler was able to optimize the tight loop that copied the sketch cells very well.

  serialize(std::ostream&)

   Buckets      Baseline      Sink API       Final    Final vs Baseline
  ━━━━━━━━━  ━━━━━━━━━━━━  ━━━━━━━━━━━━  ━━━━━━━━━━  ━━━━━━━━━━━━━━━━━━━
      1024      60512 ns      56425 ns      809 ns                74.8x
  ─────────  ────────────  ────────────  ──────────  ───────────────────
      4096     242528 ns     228888 ns     3292 ns                73.7x
  ─────────  ────────────  ────────────  ──────────  ───────────────────
     32768    1963189 ns    1863021 ns    32501 ns                60.4x
  ─────────  ────────────  ────────────  ──────────  ───────────────────
     65536    3927308 ns    3676281 ns    76400 ns                51.4x

The ostream& overload benefits from 63e7f75 a lot.

@cv4g cv4g force-pushed the countmin-serialize-to branch from de5ec55 to 91f6b19 Compare July 10, 2026 21:45
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.

1 participant