Skip to content

Conversation

@j-tyler
Copy link
Contributor

@j-tyler j-tyler commented Nov 7, 2025

Summary

In Java 8+, Java has a set of native Base64 utlities which are much more performant than the Apache Commons library.

We observed some significant memory usage coming from the Base64 usaged in the codebase, and thus migrating to the Java native version will reduce memory allocation.

In addition, this PR introduces a JMH benchmarking module which can be run via ./gradlew jmh. This module allows meaningful performance testing locally, allowing anyone to verify the performance gains asserted in a PR are actually true. We start out with a set of simple benchmarks showing the significant performance difference between the two Base64 implementations across various byte sizes.

On my M4 MacBook Encode is about 30x faster and Decode 5x faster.

Testing Done

./gradlew build passed with no changes other than swapping in the new library.

Benchmark results from my M4 MacBook on Friday Nov 7 2025. This is just a single sample
but the difference is in an order of magnitude.

Benchmark                                                             (blobSize)   Mode  Cnt         Score   Error   Units
Base64Benchmark.apacheCommonsDecode                                         1024  thrpt    2       394.913          ops/ms
Base64Benchmark.apacheCommonsDecode:·gc.alloc.rate                          1024  thrpt    2      3672.786          MB/sec
Base64Benchmark.apacheCommonsDecode:·gc.alloc.rate.norm                     1024  thrpt    2     10736.000            B/op
Base64Benchmark.apacheCommonsDecode:·gc.churn.G1_Eden_Space                 1024  thrpt    2      3675.832          MB/sec
Base64Benchmark.apacheCommonsDecode:·gc.churn.G1_Eden_Space.norm            1024  thrpt    2     10749.341            B/op
Base64Benchmark.apacheCommonsDecode:·gc.churn.G1_Old_Gen                    1024  thrpt    2         0.003          MB/sec
Base64Benchmark.apacheCommonsDecode:·gc.churn.G1_Old_Gen.norm               1024  thrpt    2         0.010            B/op
Base64Benchmark.apacheCommonsDecode:·gc.count                               1024  thrpt    2        33.000          counts
Base64Benchmark.apacheCommonsDecode:·gc.time                                1024  thrpt    2        26.000              ms
Base64Benchmark.apacheCommonsDecode                                       131072  thrpt    2         3.136          ops/ms
Base64Benchmark.apacheCommonsDecode:·gc.alloc.rate                        131072  thrpt    2      2232.963          MB/sec
Base64Benchmark.apacheCommonsDecode:·gc.alloc.rate.norm                   131072  thrpt    2    822168.028            B/op
Base64Benchmark.apacheCommonsDecode:·gc.churn.G1_Eden_Space               131072  thrpt    2      2226.617          MB/sec
Base64Benchmark.apacheCommonsDecode:·gc.churn.G1_Eden_Space.norm          131072  thrpt    2    819831.627            B/op
Base64Benchmark.apacheCommonsDecode:·gc.churn.G1_Old_Gen                  131072  thrpt    2         0.255          MB/sec
Base64Benchmark.apacheCommonsDecode:·gc.churn.G1_Old_Gen.norm             131072  thrpt    2        93.910            B/op
Base64Benchmark.apacheCommonsDecode:·gc.count                             131072  thrpt    2        20.000          counts
Base64Benchmark.apacheCommonsDecode:·gc.time                              131072  thrpt    2        17.000              ms
Base64Benchmark.apacheCommonsDecode                                      4194304  thrpt    2         0.090          ops/ms
Base64Benchmark.apacheCommonsDecode:·gc.alloc.rate                       4194304  thrpt    2      2064.650          MB/sec
Base64Benchmark.apacheCommonsDecode:·gc.alloc.rate.norm                  4194304  thrpt    2  26556048.979            B/op
Base64Benchmark.apacheCommonsDecode:·gc.churn.G1_Eden_Space              4194304  thrpt    2        43.166          MB/sec
Base64Benchmark.apacheCommonsDecode:·gc.churn.G1_Eden_Space.norm         4194304  thrpt    2    555121.754            B/op
Base64Benchmark.apacheCommonsDecode:·gc.churn.G1_Old_Gen                 4194304  thrpt    2      2474.487          MB/sec
Base64Benchmark.apacheCommonsDecode:·gc.churn.G1_Old_Gen.norm            4194304  thrpt    2  31822437.668            B/op
Base64Benchmark.apacheCommonsDecode:·gc.count                            4194304  thrpt    2        37.000          counts
Base64Benchmark.apacheCommonsDecode:·gc.time                             4194304  thrpt    2        16.000              ms
Base64Benchmark.apacheCommonsEncode                                         1024  thrpt    2       158.717          ops/ms
Base64Benchmark.apacheCommonsEncode:·gc.alloc.rate                          1024  thrpt    2      1526.268          MB/sec
Base64Benchmark.apacheCommonsEncode:·gc.alloc.rate.norm                     1024  thrpt    2     11104.001            B/op
Base64Benchmark.apacheCommonsEncode:·gc.churn.G1_Eden_Space                 1024  thrpt    2      1557.234          MB/sec
Base64Benchmark.apacheCommonsEncode:·gc.churn.G1_Eden_Space.norm            1024  thrpt    2     11331.244            B/op
Base64Benchmark.apacheCommonsEncode:·gc.churn.G1_Old_Gen                    1024  thrpt    2         0.019          MB/sec
Base64Benchmark.apacheCommonsEncode:·gc.churn.G1_Old_Gen.norm               1024  thrpt    2         0.140            B/op
Base64Benchmark.apacheCommonsEncode:·gc.churn.G1_Survivor_Space             1024  thrpt    2         0.272          MB/sec
Base64Benchmark.apacheCommonsEncode:·gc.churn.G1_Survivor_Space.norm        1024  thrpt    2         1.957            B/op
Base64Benchmark.apacheCommonsEncode:·gc.count                               1024  thrpt    2        14.000          counts
Base64Benchmark.apacheCommonsEncode:·gc.time                                1024  thrpt    2        21.000              ms
Base64Benchmark.apacheCommonsEncode                                       131072  thrpt    2         1.776          ops/ms
Base64Benchmark.apacheCommonsEncode:·gc.alloc.rate                        131072  thrpt    2      1331.554          MB/sec
Base64Benchmark.apacheCommonsEncode:·gc.alloc.rate.norm                   131072  thrpt    2    865888.051            B/op
Base64Benchmark.apacheCommonsEncode:·gc.churn.G1_Eden_Space               131072  thrpt    2      1336.168          MB/sec
Base64Benchmark.apacheCommonsEncode:·gc.churn.G1_Eden_Space.norm          131072  thrpt    2    869085.048            B/op
Base64Benchmark.apacheCommonsEncode:·gc.churn.G1_Old_Gen                  131072  thrpt    2         0.038          MB/sec
Base64Benchmark.apacheCommonsEncode:·gc.churn.G1_Old_Gen.norm             131072  thrpt    2        25.055            B/op
Base64Benchmark.apacheCommonsEncode:·gc.count                             131072  thrpt    2        12.000          counts
Base64Benchmark.apacheCommonsEncode:·gc.time                              131072  thrpt    2        10.000              ms
Base64Benchmark.apacheCommonsEncode                                      4194304  thrpt    2         0.056          ops/ms
Base64Benchmark.apacheCommonsEncode:·gc.alloc.rate                       4194304  thrpt    2      1350.299          MB/sec
Base64Benchmark.apacheCommonsEncode:·gc.alloc.rate.norm                  4194304  thrpt    2  27954177.574            B/op
Base64Benchmark.apacheCommonsEncode:·gc.churn.G1_Eden_Space              4194304  thrpt    2        26.820          MB/sec
Base64Benchmark.apacheCommonsEncode:·gc.churn.G1_Eden_Space.norm         4194304  thrpt    2    555372.879            B/op
Base64Benchmark.apacheCommonsEncode:·gc.churn.G1_Old_Gen                 4194304  thrpt    2      1597.704          MB/sec
Base64Benchmark.apacheCommonsEncode:·gc.churn.G1_Old_Gen.norm            4194304  thrpt    2  33080946.451            B/op
Base64Benchmark.apacheCommonsEncode:·gc.churn.G1_Survivor_Space          4194304  thrpt    2         0.272          MB/sec
Base64Benchmark.apacheCommonsEncode:·gc.churn.G1_Survivor_Space.norm     4194304  thrpt    2      5698.783            B/op
Base64Benchmark.apacheCommonsEncode:·gc.count                            4194304  thrpt    2        24.000          counts
Base64Benchmark.apacheCommonsEncode:·gc.time                             4194304  thrpt    2        15.000              ms
Base64Benchmark.java8Decode                                                 1024  thrpt    2      1904.665          ops/ms
Base64Benchmark.java8Decode:·gc.alloc.rate                                  1024  thrpt    2      3998.270          MB/sec
Base64Benchmark.java8Decode:·gc.alloc.rate.norm                             1024  thrpt    2      2424.000            B/op
Base64Benchmark.java8Decode:·gc.churn.G1_Eden_Space                         1024  thrpt    2      4008.441          MB/sec
Base64Benchmark.java8Decode:·gc.churn.G1_Eden_Space.norm                    1024  thrpt    2      2430.167            B/op
Base64Benchmark.java8Decode:·gc.churn.G1_Old_Gen                            1024  thrpt    2         0.004          MB/sec
Base64Benchmark.java8Decode:·gc.churn.G1_Old_Gen.norm                       1024  thrpt    2         0.002            B/op
Base64Benchmark.java8Decode:·gc.count                                       1024  thrpt    2        36.000          counts
Base64Benchmark.java8Decode:·gc.time                                        1024  thrpt    2        28.000              ms
Base64Benchmark.java8Decode                                               131072  thrpt    2        15.906          ops/ms
Base64Benchmark.java8Decode:·gc.alloc.rate                                131072  thrpt    2      4213.361          MB/sec
Base64Benchmark.java8Decode:·gc.alloc.rate.norm                           131072  thrpt    2    305872.006            B/op
Base64Benchmark.java8Decode:·gc.churn.G1_Eden_Space                       131072  thrpt    2      4788.915          MB/sec
Base64Benchmark.java8Decode:·gc.churn.G1_Eden_Space.norm                  131072  thrpt    2    347702.866            B/op
Base64Benchmark.java8Decode:·gc.churn.G1_Old_Gen                          131072  thrpt    2         0.012          MB/sec
Base64Benchmark.java8Decode:·gc.churn.G1_Old_Gen.norm                     131072  thrpt    2         0.891            B/op
Base64Benchmark.java8Decode:·gc.count                                     131072  thrpt    2        43.000          counts
Base64Benchmark.java8Decode:·gc.time                                      131072  thrpt    2        31.000              ms
Base64Benchmark.java8Decode                                              4194304  thrpt    2         0.507          ops/ms
Base64Benchmark.java8Decode:·gc.alloc.rate                               4194304  thrpt    2      4299.301          MB/sec
Base64Benchmark.java8Decode:·gc.alloc.rate.norm                          4194304  thrpt    2   9786744.195            B/op
Base64Benchmark.java8Decode:·gc.churn.G1_Eden_Space                      4194304  thrpt    2         6.985          MB/sec
Base64Benchmark.java8Decode:·gc.churn.G1_Eden_Space.norm                 4194304  thrpt    2     15899.632            B/op
Base64Benchmark.java8Decode:·gc.churn.G1_Old_Gen                         4194304  thrpt    2      5073.627          MB/sec
Base64Benchmark.java8Decode:·gc.churn.G1_Old_Gen.norm                    4194304  thrpt    2  11548859.046            B/op
Base64Benchmark.java8Decode:·gc.count                                    4194304  thrpt    2        75.000          counts
Base64Benchmark.java8Decode:·gc.time                                     4194304  thrpt    2        33.000              ms
Base64Benchmark.java8Encode                                                 1024  thrpt    2      9567.254          ops/ms
Base64Benchmark.java8Encode:·gc.alloc.rate                                  1024  thrpt    2     23134.075          MB/sec
Base64Benchmark.java8Encode:·gc.alloc.rate.norm                             1024  thrpt    2      2792.000            B/op
Base64Benchmark.java8Encode:·gc.churn.G1_Eden_Space                         1024  thrpt    2     23266.894          MB/sec
Base64Benchmark.java8Encode:·gc.churn.G1_Eden_Space.norm                    1024  thrpt    2      2807.935            B/op
Base64Benchmark.java8Encode:·gc.churn.G1_Old_Gen                            1024  thrpt    2         0.016          MB/sec
Base64Benchmark.java8Encode:·gc.churn.G1_Old_Gen.norm                       1024  thrpt    2         0.002            B/op
Base64Benchmark.java8Encode:·gc.count                                       1024  thrpt    2       209.000          counts
Base64Benchmark.java8Encode:·gc.time                                        1024  thrpt    2       144.000              ms
Base64Benchmark.java8Encode                                               131072  thrpt    2        80.678          ops/ms
Base64Benchmark.java8Encode:·gc.alloc.rate                                131072  thrpt    2     24427.684          MB/sec
Base64Benchmark.java8Encode:·gc.alloc.rate.norm                           131072  thrpt    2    349592.001            B/op
Base64Benchmark.java8Encode:·gc.churn.G1_Eden_Space                       131072  thrpt    2     29395.129          MB/sec
Base64Benchmark.java8Encode:·gc.churn.G1_Eden_Space.norm                  131072  thrpt    2    420677.021            B/op
Base64Benchmark.java8Encode:·gc.churn.G1_Old_Gen                          131072  thrpt    2         0.029          MB/sec
Base64Benchmark.java8Encode:·gc.churn.G1_Old_Gen.norm                     131072  thrpt    2         0.410            B/op
Base64Benchmark.java8Encode:·gc.count                                     131072  thrpt    2       264.000          counts
Base64Benchmark.java8Encode:·gc.time                                      131072  thrpt    2       186.000              ms
Base64Benchmark.java8Encode                                              4194304  thrpt    2         1.749          ops/ms
Base64Benchmark.java8Encode:·gc.alloc.rate                               4194304  thrpt    2     16937.686          MB/sec
Base64Benchmark.java8Encode:·gc.alloc.rate.norm                          4194304  thrpt    2  11184872.079            B/op
Base64Benchmark.java8Encode:·gc.churn.G1_Eden_Space                      4194304  thrpt    2        26.229          MB/sec
Base64Benchmark.java8Encode:·gc.churn.G1_Eden_Space.norm                 4194304  thrpt    2     17502.187            B/op
Base64Benchmark.java8Encode:·gc.churn.G1_Old_Gen                         4194304  thrpt    2     19016.132          MB/sec
Base64Benchmark.java8Encode:·gc.churn.G1_Old_Gen.norm                    4194304  thrpt    2  12527808.454            B/op
Base64Benchmark.java8Encode:·gc.count                                    4194304  thrpt    2       282.000          counts
Base64Benchmark.java8Encode:·gc.time                                     4194304  thrpt    2       119.000              ms

In Java 8+, Java has a set of native Base64 utlities
which are much more performant than the Apache Commons library.

We observed some significant memory usage coming from the Base64 usaged in the
codebase, and thus migrating to the Java native version will reduce memory allocation.

In addition, this PR introduces a JMH benchmarking module which can be run via
./gradlew jmh. This module allows meaningful performance testing locally, allowing
anyone to verify the performance gains asserted in a PR are actually true. We start
out with a set of simple benchmarks showing the significant performance difference
between the two Base64 implementations across various byte sizes.

On my M4 MacBook Encode is about 30x faster and Decode 5x faster.
@codecov-commenter
Copy link

codecov-commenter commented Nov 12, 2025

Codecov Report

❌ Patch coverage is 96.29630% with 1 line in your changes missing coverage. Please review.
✅ Project coverage is 69.85%. Comparing base (52ba813) to head (06e4626).
⚠️ Report is 318 commits behind head on master.

Files with missing lines Patch % Lines
...ls/src/main/java/com/github/ambry/utils/Utils.java 85.71% 1 Missing ⚠️
Additional details and impacted files
@@             Coverage Diff              @@
##             master    #3166      +/-   ##
============================================
+ Coverage     64.24%   69.85%   +5.60%     
- Complexity    10398    12786    +2388     
============================================
  Files           840      930      +90     
  Lines         71755    78926    +7171     
  Branches       8611     9427     +816     
============================================
+ Hits          46099    55133    +9034     
+ Misses        23004    20870    -2134     
- Partials       2652     2923     +271     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@j-tyler j-tyler force-pushed the j-tyler/base64-with-jmh branch from e4b48ba to ba77e76 Compare November 12, 2025 20:00
Apache 2 Base64 decode has special leniency for invalid encodings. To ensure
full backwards compat we fall back to using Apache 2 when decoding fails.
@justinlin-linkedin justinlin-linkedin merged commit 2ce9093 into linkedin:master Nov 13, 2025
5 checks passed
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