Skip to content

Conversation

@chaokunyang
Copy link
Collaborator

@chaokunyang chaokunyang commented Jan 26, 2025

What does this PR do?

This pr provides a new implementation for chunk based map serialization.

Related issues

#1571
#1549
#1722
Closes #925

Does this PR introduce any user-facing change?

  • Does this PR introduce any public API change?
  • Does this PR introduce any binary protocol compatibility change?

Benchmark

Deserialization are much faster than no-chunk version, serialization are faster if map size are bigger

Using the benchmark code in #1722 (comment):

This PR has run faster, it gets up to 3x faster :

Benchmark                                  (size)  (tracking)  Mode  Cnt     Score       Error  Units
HnBenchmark.testGeneralChunkWriteWithNull      64        true  avgt    3   965.521 ±  1830.936  ns/op
HnBenchmark.testGeneralChunkWriteWithNull      64       false  avgt    3  1060.411 ±  3424.719  ns/op
HnBenchmark.testGeneralChunkWriteWithNull     128        true  avgt    3  2404.445 ±  8687.122  ns/op
HnBenchmark.testGeneralChunkWriteWithNull     128       false  avgt    3  1814.507 ±  1722.751  ns/op
HnBenchmark.testGeneralChunkWriteWithNull     256        true  avgt    3  3944.632 ±  2203.076  ns/op
HnBenchmark.testGeneralChunkWriteWithNull     256       false  avgt    3  3288.805 ±   867.047  ns/op
HnBenchmark.testGeneralWriteWithNull           64        true  avgt    3  1962.688 ±  2828.210  ns/op
HnBenchmark.testGeneralWriteWithNull           64       false  avgt    3  1490.634 ±   962.836  ns/op
HnBenchmark.testGeneralWriteWithNull          128        true  avgt    3  3659.806 ±  7227.436  ns/op
HnBenchmark.testGeneralWriteWithNull          128       false  avgt    3  4084.654 ±  7374.774  ns/op
HnBenchmark.testGeneralWriteWithNull          256        true  avgt    3  9596.658 ± 20767.262  ns/op
HnBenchmark.testGeneralWriteWithNull          256       false  avgt    3  6679.325 ±  5472.179  ns/op

With StringMap and IntMap benchmark:

Benchmark                                   (enableChunkEncoding)  (mapSize)   Mode  Cnt        Score          Error  Units
MapSerializationSuite.deserializeIntMap                     false          5  thrpt    3  3804604.842 ± 15328547.705  ops/s
MapSerializationSuite.deserializeIntMap                     false         20  thrpt    3  1254687.969 ±   388949.724  ops/s
MapSerializationSuite.deserializeIntMap                     false         50  thrpt    3   495176.849 ±   335702.097  ops/s
MapSerializationSuite.deserializeIntMap                     false        100  thrpt    3   258875.012 ±    32886.176  ops/s
MapSerializationSuite.deserializeIntMap                     false        200  thrpt    3   134137.015 ±   114908.454  ops/s
MapSerializationSuite.deserializeIntMap                      true          5  thrpt    3  5997383.562 ±  4598913.048  ops/s
MapSerializationSuite.deserializeIntMap                      true         20  thrpt    3  1797855.524 ±  3853406.173  ops/s
MapSerializationSuite.deserializeIntMap                      true         50  thrpt    3   582412.110 ±  1047668.070  ops/s
MapSerializationSuite.deserializeIntMap                      true        100  thrpt    3   389066.866 ±   151297.708  ops/s
MapSerializationSuite.deserializeIntMap                      true        200  thrpt    3   188316.860 ±    35331.909  ops/s
MapSerializationSuite.deserializeStringMap                  false          5  thrpt    3  2898963.533 ±  1930240.310  ops/s
MapSerializationSuite.deserializeStringMap                  false         20  thrpt    3   872196.086 ±   871637.268  ops/s
MapSerializationSuite.deserializeStringMap                  false         50  thrpt    3   308761.737 ±    58099.196  ops/s
MapSerializationSuite.deserializeStringMap                  false        100  thrpt    3   157261.914 ±   397356.241  ops/s
MapSerializationSuite.deserializeStringMap                  false        200  thrpt    3    86576.549 ±   102489.156  ops/s
MapSerializationSuite.deserializeStringMap                   true          5  thrpt    3  3701089.567 ±  1529899.331  ops/s
MapSerializationSuite.deserializeStringMap                   true         20  thrpt    3  1048550.399 ±   130102.760  ops/s
MapSerializationSuite.deserializeStringMap                   true         50  thrpt    3   407559.246 ±    38205.273  ops/s
MapSerializationSuite.deserializeStringMap                   true        100  thrpt    3   172109.437 ±   397927.346  ops/s
MapSerializationSuite.deserializeStringMap                   true        200  thrpt    3    92525.977 ±   379321.772  ops/s
MapSerializationSuite.serializeIntMap                       false          5  thrpt    3  7958692.983 ±  1934287.574  ops/s
MapSerializationSuite.serializeIntMap                       false         20  thrpt    3  2425269.897 ±  3763706.776  ops/s
MapSerializationSuite.serializeIntMap                       false         50  thrpt    3  1079804.122 ±   215967.411  ops/s
MapSerializationSuite.serializeIntMap                       false        100  thrpt    3   369848.671 ±   433172.821  ops/s
MapSerializationSuite.serializeIntMap                       false        200  thrpt    3   192858.945 ±    71543.709  ops/s
MapSerializationSuite.serializeIntMap                        true          5  thrpt    3  7239453.648 ±  3855324.170  ops/s
MapSerializationSuite.serializeIntMap                        true         20  thrpt    3  2137006.685 ±  3823762.656  ops/s
MapSerializationSuite.serializeIntMap                        true         50  thrpt    3   811639.511 ±  2407986.801  ops/s
MapSerializationSuite.serializeIntMap                        true        100  thrpt    3   412728.569 ±   149199.142  ops/s
MapSerializationSuite.serializeIntMap                        true        200  thrpt    3   236602.475 ±   253662.098  ops/s
MapSerializationSuite.serializeStringMap                    false          5  thrpt    3  5821603.026 ±  1397740.496  ops/s
MapSerializationSuite.serializeStringMap                    false         20  thrpt    3  1712819.341 ±   321017.433  ops/s
MapSerializationSuite.serializeStringMap                    false         50  thrpt    3   615260.241 ±   806075.165  ops/s
MapSerializationSuite.serializeStringMap                    false        100  thrpt    3   265117.558 ±   146904.745  ops/s
MapSerializationSuite.serializeStringMap                    false        200  thrpt    3   128618.697 ±    94723.953  ops/s
MapSerializationSuite.serializeStringMap                     true          5  thrpt    3  4503474.325 ± 11254674.336  ops/s
MapSerializationSuite.serializeStringMap                     true         20  thrpt    3  1732501.942 ±   373691.778  ops/s
MapSerializationSuite.serializeStringMap                     true         50  thrpt    3   596678.154 ±   173893.988  ops/s
MapSerializationSuite.serializeStringMap                     true        100  thrpt    3   336814.584 ±   134582.563  ops/s
MapSerializationSuite.serializeStringMap                     true        200  thrpt    3   143124.619 ±   200889.695  ops/s

image
image
image
image

@chaokunyang chaokunyang changed the title feat(java): new implementation for chunk based map serialization feat(java): new implementation and protocol refine for chunk based map serialization Jan 26, 2025
@pandalee99 pandalee99 self-requested a review January 26, 2025 07:03
@chaokunyang
Copy link
Collaborator Author

plot script:

import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
import io

data = """
"Benchmark","Mode","Threads","Samples","Score","Score Error","Unit","enableChunkEncoding","mapSize"
"org.apache.fury.benchmark.MapSerializationSuite.deserializeIntMap","thrpt",1,9,4466051.463287,873291.405075,"ops/s",false,5
"org.apache.fury.benchmark.MapSerializationSuite.deserializeIntMap","thrpt",1,9,1350641.974999,81715.924297,"ops/s",false,20
"org.apache.fury.benchmark.MapSerializationSuite.deserializeIntMap","thrpt",1,9,497254.149014,68862.874865,"ops/s",false,50
"org.apache.fury.benchmark.MapSerializationSuite.deserializeIntMap","thrpt",1,9,266334.668251,35573.243688,"ops/s",false,100
"org.apache.fury.benchmark.MapSerializationSuite.deserializeIntMap","thrpt",1,9,122097.002001,27652.497466,"ops/s",false,200
"org.apache.fury.benchmark.MapSerializationSuite.deserializeIntMap","thrpt",1,9,5888333.717480,739656.066973,"ops/s",true,5
"org.apache.fury.benchmark.MapSerializationSuite.deserializeIntMap","thrpt",1,9,2036689.629240,254260.659777,"ops/s",true,20
"org.apache.fury.benchmark.MapSerializationSuite.deserializeIntMap","thrpt",1,9,734567.196053,49056.877252,"ops/s",true,50
"org.apache.fury.benchmark.MapSerializationSuite.deserializeIntMap","thrpt",1,9,367623.736912,59614.675972,"ops/s",true,100
"org.apache.fury.benchmark.MapSerializationSuite.deserializeIntMap","thrpt",1,9,170496.570903,32886.215740,"ops/s",true,200
"org.apache.fury.benchmark.MapSerializationSuite.serializeIntMap","thrpt",1,9,7893446.540629,808837.061250,"ops/s",false,5
"org.apache.fury.benchmark.MapSerializationSuite.serializeIntMap","thrpt",1,9,2584359.305934,129956.799737,"ops/s",false,20
"org.apache.fury.benchmark.MapSerializationSuite.serializeIntMap","thrpt",1,9,1036138.030458,84703.530824,"ops/s",false,50
"org.apache.fury.benchmark.MapSerializationSuite.serializeIntMap","thrpt",1,9,394205.916586,25123.147428,"ops/s",false,100
"org.apache.fury.benchmark.MapSerializationSuite.serializeIntMap","thrpt",1,9,206653.480783,30347.851656,"ops/s",false,200
"org.apache.fury.benchmark.MapSerializationSuite.serializeIntMap","thrpt",1,9,7337531.043356,437811.548408,"ops/s",true,5
"org.apache.fury.benchmark.MapSerializationSuite.serializeIntMap","thrpt",1,9,2474583.537284,379139.624785,"ops/s",true,20
"org.apache.fury.benchmark.MapSerializationSuite.serializeIntMap","thrpt",1,9,806053.192966,112383.449082,"ops/s",true,50
"org.apache.fury.benchmark.MapSerializationSuite.serializeIntMap","thrpt",1,9,434183.111785,64625.641813,"ops/s",true,100
"org.apache.fury.benchmark.MapSerializationSuite.serializeIntMap","thrpt",1,9,208393.763140,22779.649134,"ops/s",true,200
"""

# Read the data into a DataFrame
df = pd.read_csv(io.StringIO(data))

# Function to plot the data for each benchmark type
def plot_benchmark(dataframe, benchmark_name):
    # Filter the data for the given benchmark name
    df_benchmark = dataframe[dataframe['Benchmark'].str.contains(benchmark_name)]

    # Set up the matplotlib figure
    plt.figure(figsize=(10, 6))

    # Plot the scores with and without Chunk Encoding
    sns.barplot(data=df_benchmark, x='mapSize', y='Score', hue='enableChunkEncoding')

    # Add title and labels
    plt.title(f'{benchmark_name} performance')
    plt.xlabel('Map Size')
    plt.ylabel('Score (ops/s)')

    # Show the plot
    plt.legend(title='Chunk Encoding')
    plt.show()

# Plot for each benchmark type
benchmark_types = [
    "deserializeIntMap",
    "deserializeStringMap",
    "serializeIntMap",
    "serializeStringMap"
]

for benchmark in benchmark_types:
    plot_benchmark(df, benchmark)

@chaokunyang
Copy link
Collaborator Author

@theweipeng This pr udpates teh xlang map serialization protocol, the furyjs may need to udpate the implementation too.

Copy link
Contributor

@pandalee99 pandalee99 left a comment

Choose a reason for hiding this comment

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

LGTM.
This PR happens to be 2025😄
Wish you a happy new year!🥳

@pandalee99 pandalee99 merged commit e2ab164 into apache:main Jan 26, 2025
40 checks passed
chaokunyang added a commit that referenced this pull request Jan 28, 2025
## What does this PR do?
This PR added jit support for chunk based map serialization, it supports
all kinds of map serializaiton by generated code:
-  final map key and value field type
- polymorphic map key and value field type
- nested map key and value type

This PR also removed the old map serialization protocol code.


The new chunk based protocol improve serialized size by **2.3X** at
most.

data:
```
stringMap: {"k1": "v1", "k2": "v2, ..., "k10": "v10" }
intMap: {1:2, 2:4, 3: 6, ..., 10: 20}
```

new protocol:
```
stringMapBytes 68
stringKVStructBytes 69
intMapBytes 28
intKVStructBytes 29
```

old protocol:
```
stringMapBytes 104
stringKVStructBytes 87
intMapBytes 64
intKVStructBytes 47
```

And improve performance by 20%


## Related issues

Closes #925 

#2025 

## Does this PR introduce any user-facing change?

<!--
If any user-facing interface changes, please [open an
issue](https://github.com/apache/fury/issues/new/choose) describing the
need to do so and update the document if necessary.
-->

- [ ] Does this PR introduce any public API change?
- [ ] Does this PR introduce any binary protocol compatibility change?

## Benchmark


[chunk-jmh-result.csv](https://github.com/user-attachments/files/18575900/chunk-jmh-result.csv)

[nochunk-jmh-result.csv](https://github.com/user-attachments/files/18575901/nochunk-jmh-result.csv)


![image](https://github.com/user-attachments/assets/754f8e48-b45e-489b-adf5-cca1c5d03f1e)
chaokunyang added a commit that referenced this pull request Feb 6, 2025
## What does this PR do?

 fix duplicate entry write at max chunk size bound
## Related issues

#2025
#2027 

## Does this PR introduce any user-facing change?

<!--
If any user-facing interface changes, please [open an
issue](https://github.com/apache/fury/issues/new/choose) describing the
need to do so and update the document if necessary.
-->

- [ ] Does this PR introduce any public API change?
- [ ] Does this PR introduce any binary protocol compatibility change?

## Benchmark

<!--
When the PR has an impact on performance (if you don't know whether the
PR will have an impact on performance, you can submit the PR first, and
if it will have impact on performance, the code reviewer will explain
it), be sure to attach a benchmark data here.
-->
chaokunyang added a commit that referenced this pull request Feb 7, 2025
…p serialization (#2025)

This pr provides a new implementation for chunk based map serialization.

Closes #925

<!--
If any user-facing interface changes, please [open an
issue](https://github.com/apache/fury/issues/new/choose) describing the
need to do so and update the document if necessary.
-->

- [ ] Does this PR introduce any public API change?
- [ ] Does this PR introduce any binary protocol compatibility change?

Deserialization are much faster than no-chunk version, serialization are
faster if map size are bigger

Using the benchmark code in
#1722 (comment):

This PR has run faster, it gets up to **3x faster** :
```java
Benchmark                                  (size)  (tracking)  Mode  Cnt     Score       Error  Units
HnBenchmark.testGeneralChunkWriteWithNull      64        true  avgt    3   965.521 ±  1830.936  ns/op
HnBenchmark.testGeneralChunkWriteWithNull      64       false  avgt    3  1060.411 ±  3424.719  ns/op
HnBenchmark.testGeneralChunkWriteWithNull     128        true  avgt    3  2404.445 ±  8687.122  ns/op
HnBenchmark.testGeneralChunkWriteWithNull     128       false  avgt    3  1814.507 ±  1722.751  ns/op
HnBenchmark.testGeneralChunkWriteWithNull     256        true  avgt    3  3944.632 ±  2203.076  ns/op
HnBenchmark.testGeneralChunkWriteWithNull     256       false  avgt    3  3288.805 ±   867.047  ns/op
HnBenchmark.testGeneralWriteWithNull           64        true  avgt    3  1962.688 ±  2828.210  ns/op
HnBenchmark.testGeneralWriteWithNull           64       false  avgt    3  1490.634 ±   962.836  ns/op
HnBenchmark.testGeneralWriteWithNull          128        true  avgt    3  3659.806 ±  7227.436  ns/op
HnBenchmark.testGeneralWriteWithNull          128       false  avgt    3  4084.654 ±  7374.774  ns/op
HnBenchmark.testGeneralWriteWithNull          256        true  avgt    3  9596.658 ± 20767.262  ns/op
HnBenchmark.testGeneralWriteWithNull          256       false  avgt    3  6679.325 ±  5472.179  ns/op
```

With StringMap and IntMap benchmark:
```java

Benchmark                                   (enableChunkEncoding)  (mapSize)   Mode  Cnt        Score          Error  Units
MapSerializationSuite.deserializeIntMap                     false          5  thrpt    3  3804604.842 ± 15328547.705  ops/s
MapSerializationSuite.deserializeIntMap                     false         20  thrpt    3  1254687.969 ±   388949.724  ops/s
MapSerializationSuite.deserializeIntMap                     false         50  thrpt    3   495176.849 ±   335702.097  ops/s
MapSerializationSuite.deserializeIntMap                     false        100  thrpt    3   258875.012 ±    32886.176  ops/s
MapSerializationSuite.deserializeIntMap                     false        200  thrpt    3   134137.015 ±   114908.454  ops/s
MapSerializationSuite.deserializeIntMap                      true          5  thrpt    3  5997383.562 ±  4598913.048  ops/s
MapSerializationSuite.deserializeIntMap                      true         20  thrpt    3  1797855.524 ±  3853406.173  ops/s
MapSerializationSuite.deserializeIntMap                      true         50  thrpt    3   582412.110 ±  1047668.070  ops/s
MapSerializationSuite.deserializeIntMap                      true        100  thrpt    3   389066.866 ±   151297.708  ops/s
MapSerializationSuite.deserializeIntMap                      true        200  thrpt    3   188316.860 ±    35331.909  ops/s
MapSerializationSuite.deserializeStringMap                  false          5  thrpt    3  2898963.533 ±  1930240.310  ops/s
MapSerializationSuite.deserializeStringMap                  false         20  thrpt    3   872196.086 ±   871637.268  ops/s
MapSerializationSuite.deserializeStringMap                  false         50  thrpt    3   308761.737 ±    58099.196  ops/s
MapSerializationSuite.deserializeStringMap                  false        100  thrpt    3   157261.914 ±   397356.241  ops/s
MapSerializationSuite.deserializeStringMap                  false        200  thrpt    3    86576.549 ±   102489.156  ops/s
MapSerializationSuite.deserializeStringMap                   true          5  thrpt    3  3701089.567 ±  1529899.331  ops/s
MapSerializationSuite.deserializeStringMap                   true         20  thrpt    3  1048550.399 ±   130102.760  ops/s
MapSerializationSuite.deserializeStringMap                   true         50  thrpt    3   407559.246 ±    38205.273  ops/s
MapSerializationSuite.deserializeStringMap                   true        100  thrpt    3   172109.437 ±   397927.346  ops/s
MapSerializationSuite.deserializeStringMap                   true        200  thrpt    3    92525.977 ±   379321.772  ops/s
MapSerializationSuite.serializeIntMap                       false          5  thrpt    3  7958692.983 ±  1934287.574  ops/s
MapSerializationSuite.serializeIntMap                       false         20  thrpt    3  2425269.897 ±  3763706.776  ops/s
MapSerializationSuite.serializeIntMap                       false         50  thrpt    3  1079804.122 ±   215967.411  ops/s
MapSerializationSuite.serializeIntMap                       false        100  thrpt    3   369848.671 ±   433172.821  ops/s
MapSerializationSuite.serializeIntMap                       false        200  thrpt    3   192858.945 ±    71543.709  ops/s
MapSerializationSuite.serializeIntMap                        true          5  thrpt    3  7239453.648 ±  3855324.170  ops/s
MapSerializationSuite.serializeIntMap                        true         20  thrpt    3  2137006.685 ±  3823762.656  ops/s
MapSerializationSuite.serializeIntMap                        true         50  thrpt    3   811639.511 ±  2407986.801  ops/s
MapSerializationSuite.serializeIntMap                        true        100  thrpt    3   412728.569 ±   149199.142  ops/s
MapSerializationSuite.serializeIntMap                        true        200  thrpt    3   236602.475 ±   253662.098  ops/s
MapSerializationSuite.serializeStringMap                    false          5  thrpt    3  5821603.026 ±  1397740.496  ops/s
MapSerializationSuite.serializeStringMap                    false         20  thrpt    3  1712819.341 ±   321017.433  ops/s
MapSerializationSuite.serializeStringMap                    false         50  thrpt    3   615260.241 ±   806075.165  ops/s
MapSerializationSuite.serializeStringMap                    false        100  thrpt    3   265117.558 ±   146904.745  ops/s
MapSerializationSuite.serializeStringMap                    false        200  thrpt    3   128618.697 ±    94723.953  ops/s
MapSerializationSuite.serializeStringMap                     true          5  thrpt    3  4503474.325 ± 11254674.336  ops/s
MapSerializationSuite.serializeStringMap                     true         20  thrpt    3  1732501.942 ±   373691.778  ops/s
MapSerializationSuite.serializeStringMap                     true         50  thrpt    3   596678.154 ±   173893.988  ops/s
MapSerializationSuite.serializeStringMap                     true        100  thrpt    3   336814.584 ±   134582.563  ops/s
MapSerializationSuite.serializeStringMap                     true        200  thrpt    3   143124.619 ±   200889.695  ops/s
```

![image](https://github.com/user-attachments/assets/a76301ea-0230-495e-afe9-9d1acea6263d)

![image](https://github.com/user-attachments/assets/2e17d0d0-6851-467d-822b-5a9024252bc4)

![image](https://github.com/user-attachments/assets/b60cdf7f-0bca-4654-a46c-c13d9443661c)

![image](https://github.com/user-attachments/assets/24637c8c-adb4-4de2-a349-df7356b7fdba)
chaokunyang added a commit that referenced this pull request Feb 7, 2025
## What does this PR do?
This PR added jit support for chunk based map serialization, it supports
all kinds of map serializaiton by generated code:
-  final map key and value field type
- polymorphic map key and value field type
- nested map key and value type

This PR also removed the old map serialization protocol code.


The new chunk based protocol improve serialized size by **2.3X** at
most.

data:
```
stringMap: {"k1": "v1", "k2": "v2, ..., "k10": "v10" }
intMap: {1:2, 2:4, 3: 6, ..., 10: 20}
```

new protocol:
```
stringMapBytes 68
stringKVStructBytes 69
intMapBytes 28
intKVStructBytes 29
```

old protocol:
```
stringMapBytes 104
stringKVStructBytes 87
intMapBytes 64
intKVStructBytes 47
```

And improve performance by 20%


## Related issues

Closes #925 

#2025 

## Does this PR introduce any user-facing change?

<!--
If any user-facing interface changes, please [open an
issue](https://github.com/apache/fury/issues/new/choose) describing the
need to do so and update the document if necessary.
-->

- [ ] Does this PR introduce any public API change?
- [ ] Does this PR introduce any binary protocol compatibility change?

## Benchmark


[chunk-jmh-result.csv](https://github.com/user-attachments/files/18575900/chunk-jmh-result.csv)

[nochunk-jmh-result.csv](https://github.com/user-attachments/files/18575901/nochunk-jmh-result.csv)


![image](https://github.com/user-attachments/assets/754f8e48-b45e-489b-adf5-cca1c5d03f1e)
chaokunyang added a commit that referenced this pull request Feb 7, 2025
## What does this PR do?

 fix duplicate entry write at max chunk size bound
## Related issues

#2025
#2027 

## Does this PR introduce any user-facing change?

<!--
If any user-facing interface changes, please [open an
issue](https://github.com/apache/fury/issues/new/choose) describing the
need to do so and update the document if necessary.
-->

- [ ] Does this PR introduce any public API change?
- [ ] Does this PR introduce any binary protocol compatibility change?

## Benchmark

<!--
When the PR has an impact on performance (if you don't know whether the
PR will have an impact on performance, you can submit the PR first, and
if it will have impact on performance, the code reviewer will explain
it), be sure to attach a benchmark data here.
-->
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.

[Java][Protocol] Chunk by chunk predictive map serialization protocol

2 participants