Skip to content

Base64 faster encode #1157

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 7 commits into from
Feb 20, 2025
Merged

Base64 faster encode #1157

merged 7 commits into from
Feb 20, 2025

Conversation

fabianfett
Copy link
Contributor

This pr should be rebased after #1155 has landed.
I'll post benchmark comparisons soon.

@fabianfett
Copy link
Contributor Author

================
Toolchain
================

base64-encode-1MB-toData-noOptions
╒═══════════════════════════╤═════════╤═════════╤═════════╤═════════╤═════════╤═════════╤═════════╤═════════╕
│ Metric                    │      p0 │     p25 │     p50 │     p75 │     p90 │     p99 │    p100 │ Samples │
╞═══════════════════════════╪═════════╪═════════╪═════════╪═════════╪═════════╪═════════╪═════════╪═════════╡
│ Malloc (total) *          │       5 │       5 │       5 │       5 │       5 │       5 │       6 │    1010 │
├───────────────────────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┤
│ Throughput (# / s) (#)    │     348 │     344 │     342 │     339 │     332 │     306 │     286 │    1010 │
├───────────────────────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┤
│ Time (total CPU) (μs) *   │    2894 │    2933 │    2949 │    2976 │    3031 │    3293 │    3519 │    1010 │
╘═══════════════════════════╧═════════╧═════════╧═════════╧═════════╧═════════╧═════════╧═════════╧═════════╛

base64-encode-1MB-toString-lineLength64
╒═══════════════════════════╤═════════╤═════════╤═════════╤═════════╤═════════╤═════════╤═════════╤═════════╕
│ Metric                    │      p0 │     p25 │     p50 │     p75 │     p90 │     p99 │    p100 │ Samples │
╞═══════════════════════════╪═════════╪═════════╪═════════╪═════════╪═════════╪═════════╪═════════╪═════════╡
│ Malloc (total) *          │       3 │       3 │       3 │       3 │       3 │       3 │       6 │     931 │
├───────────────────────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┤
│ Throughput (# / s) (#)    │     320 │     316 │     315 │     312 │     308 │     280 │     266 │     931 │
├───────────────────────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┤
│ Time (total CPU) (μs) *   │    3150 │    3183 │    3199 │    3228 │    3273 │    3594 │    3780 │     931 │
╘═══════════════════════════╧═════════╧═════════╧═════════╧═════════╧═════════╧═════════╧═════════╧═════════╛

base64-encode-1MB-toString-noOptions
╒═══════════════════════════╤═════════╤═════════╤═════════╤═════════╤═════════╤═════════╤═════════╤═════════╕
│ Metric                    │      p0 │     p25 │     p50 │     p75 │     p90 │     p99 │    p100 │ Samples │
╞═══════════════════════════╪═════════╪═════════╪═════════╪═════════╪═════════╪═════════╪═════════╪═════════╡
│ Malloc (total) *          │       3 │       3 │       3 │       3 │       3 │       3 │       6 │    1008 │
├───────────────────────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┤
│ Throughput (# / s) (#)    │     346 │     342 │     340 │     338 │     335 │     318 │     313 │    1008 │
├───────────────────────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┤
│ Time (total CPU) (μs) *   │    2915 │    2947 │    2961 │    2982 │    3011 │    3156 │    3196 │    1008 │
╘═══════════════════════════╧═════════╧═════════╧═════════╧═════════╧═════════╧═════════╧═════════╧═════════╛

base64-encode-jwtHeader-toString-noOptions
╒═══════════════════════════╤═════════╤═════════╤═════════╤═════════╤═════════╤═════════╤═════════╤═════════╕
│ Metric                    │      p0 │     p25 │     p50 │     p75 │     p90 │     p99 │    p100 │ Samples │
╞═══════════════════════════╪═════════╪═════════╪═════════╪═════════╪═════════╪═════════╪═════════╪═════════╡
│ Malloc (total) *          │       3 │       3 │       3 │       3 │       3 │       3 │       3 │    7056 │
├───────────────────────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┤
│ Throughput (# / s) (K)    │    2574 │    2535 │    2505 │    2471 │    2417 │    2243 │    1881 │    7056 │
├───────────────────────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┤
│ Time (total CPU) (ns) *   │     409 │     415 │     420 │     425 │     435 │     467 │     540 │    7056 │
╘═══════════════════════════╧═════════╧═════════╧═════════╧═════════╧═════════╧═════════╧═════════╧═════════╛

---------------------------------------------------------------------------------------------------

=============================
Existing Swift implementation
=============================

base64-encode-1MB-toData-noOptions
╒═══════════════════════════╤═════════╤═════════╤═════════╤═════════╤═════════╤═════════╤═════════╤═════════╕
│ Metric                    │      p0 │     p25 │     p50 │     p75 │     p90 │     p99 │    p100 │ Samples │
╞═══════════════════════════╪═════════╪═════════╪═════════╪═════════╪═════════╪═════════╪═════════╪═════════╡
│ Malloc (total) *          │       2 │       2 │       2 │       2 │       2 │       2 │       2 │    3988 │
├───────────────────────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┤
│ Throughput (# / s) (#)    │    1424 │    1393 │    1382 │    1365 │    1332 │    1237 │    1047 │    3988 │
├───────────────────────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┤
│ Time (total CPU) (μs) *   │     723 │     738 │     744 │     754 │     772 │     826 │     975 │    3988 │
╘═══════════════════════════╧═════════╧═════════╧═════════╧═════════╧═════════╧═════════╧═════════╧═════════╛

base64-encode-1MB-toString-lineLength64
╒═══════════════════════════╤═════════╤═════════╤═════════╤═════════╤═════════╤═════════╤═════════╤═════════╕
│ Metric                    │      p0 │     p25 │     p50 │     p75 │     p90 │     p99 │    p100 │ Samples │
╞═══════════════════════════╪═════════╪═════════╪═════════╪═════════╪═════════╪═════════╪═════════╪═════════╡
│ Malloc (total) *          │       1 │       1 │       1 │       1 │       1 │       1 │       1 │    4170 │
├───────────────────────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┤
│ Throughput (# / s) (#)    │    1451 │    1451 │    1445 │    1439 │    1404 │    1301 │    1143 │    4170 │
├───────────────────────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┤
│ Time (total CPU) (μs) *   │     708 │     709 │     712 │     715 │     733 │     788 │     886 │    4170 │
╘═══════════════════════════╧═════════╧═════════╧═════════╧═════════╧═════════╧═════════╧═════════╧═════════╛

base64-encode-1MB-toString-noOptions
╒═══════════════════════════╤═════════╤═════════╤═════════╤═════════╤═════════╤═════════╤═════════╤═════════╕
│ Metric                    │      p0 │     p25 │     p50 │     p75 │     p90 │     p99 │    p100 │ Samples │
╞═══════════════════════════╪═════════╪═════════╪═════════╪═════════╪═════════╪═════════╪═════════╪═════════╡
│ Malloc (total) *          │       1 │       1 │       1 │       1 │       1 │       1 │       1 │    4661 │
├───────────────────────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┤
│ Throughput (# / s) (#)    │    1624 │    1623 │    1619 │    1614 │    1592 │    1488 │    1091 │    4661 │
├───────────────────────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┤
│ Time (total CPU) (μs) *   │     635 │     636 │     638 │     641 │     649 │     696 │     855 │    4661 │
╘═══════════════════════════╧═════════╧═════════╧═════════╧═════════╧═════════╧═════════╧═════════╧═════════╛

base64-encode-jwtHeader-toString-noOptions
╒═══════════════════════════╤═════════╤═════════╤═════════╤═════════╤═════════╤═════════╤═════════╤═════════╕
│ Metric                    │      p0 │     p25 │     p50 │     p75 │     p90 │     p99 │    p100 │ Samples │
╞═══════════════════════════╪═════════╪═════════╪═════════╪═════════╪═════════╪═════════╪═════════╪═════════╡
│ Malloc (total) *          │       1 │       1 │       1 │       1 │       1 │       1 │       1 │   10000 │
├───────────────────────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┤
│ Throughput (# / s) (K)    │    7717 │    7707 │    7703 │    7691 │    7423 │    6571 │    4310 │   10000 │
├───────────────────────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┤
│ Time (total CPU) (ns) *   │     149 │     150 │     150 │     151 │     156 │     173 │     254 │   10000 │
╘═══════════════════════════╧═════════╧═════════╧═════════╧═════════╧═════════╧═════════╧═════════╧═════════╛

---------------------------------------------------------------------------------------------------

=============================
This PR
=============================

base64-encode-1MB-toData-noOptions
╒═══════════════════════════╤═════════╤═════════╤═════════╤═════════╤═════════╤═════════╤═════════╤═════════╕
│ Metric                    │      p0 │     p25 │     p50 │     p75 │     p90 │     p99 │    p100 │ Samples │
╞═══════════════════════════╪═════════╪═════════╪═════════╪═════════╪═════════╪═════════╪═════════╪═════════╡
│ Malloc (total) *          │       2 │       2 │       2 │       2 │       2 │       2 │       2 │    5675 │
├───────────────────────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┤
│ Throughput (# / s) (#)    │    2111 │    2057 │    2013 │    1944 │    1848 │    1674 │    1263 │    5675 │
├───────────────────────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┤
│ Time (total CPU) (μs) *   │     494 │     507 │     517 │     536 │     562 │     614 │     820 │    5675 │
╘═══════════════════════════╧═════════╧═════════╧═════════╧═════════╧═════════╧═════════╧═════════╧═════════╛

base64-encode-1MB-toString-lineLength64
╒═══════════════════════════╤═════════╤═════════╤═════════╤═════════╤═════════╤═════════╤═════════╤═════════╕
│ Metric                    │      p0 │     p25 │     p50 │     p75 │     p90 │     p99 │    p100 │ Samples │
╞═══════════════════════════╪═════════╪═════════╪═════════╪═════════╪═════════╪═════════╪═════════╪═════════╡
│ Malloc (total) *          │       1 │       1 │       1 │       1 │       1 │       1 │       1 │    5902 │
├───────────────────────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┤
│ Throughput (# / s) (#)    │    2094 │    2079 │    2067 │    2057 │    2017 │    1915 │    1589 │    5902 │
├───────────────────────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┤
│ Time (total CPU) (μs) *   │     497 │     501 │     504 │     506 │     516 │     543 │     657 │    5902 │
╘═══════════════════════════╧═════════╧═════════╧═════════╧═════════╧═════════╧═════════╧═════════╧═════════╛

base64-encode-1MB-toString-noOptions
╒═══════════════════════════╤═════════╤═════════╤═════════╤═════════╤═════════╤═════════╤═════════╤═════════╕
│ Metric                    │      p0 │     p25 │     p50 │     p75 │     p90 │     p99 │    p100 │ Samples │
╞═══════════════════════════╪═════════╪═════════╪═════════╪═════════╪═════════╪═════════╪═════════╪═════════╡
│ Malloc (total) *          │       1 │       1 │       1 │       1 │       1 │       1 │       1 │    6948 │
├───────────────────────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┤
│ Throughput (# / s) (#)    │    2532 │    2475 │    2461 │    2449 │    2387 │    2177 │    1695 │    6948 │
├───────────────────────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┤
│ Time (total CPU) (μs) *   │     414 │     424 │     426 │     429 │     440 │     479 │     592 │    6948 │
╘═══════════════════════════╧═════════╧═════════╧═════════╧═════════╧═════════╧═════════╧═════════╧═════════╛

base64-encode-jwtHeader-toString-noOptions
╒═══════════════════════════╤═════════╤═════════╤═════════╤═════════╤═════════╤═════════╤═════════╤═════════╕
│ Metric                    │      p0 │     p25 │     p50 │     p75 │     p90 │     p99 │    p100 │ Samples │
╞═══════════════════════════╪═════════╪═════════╪═════════╪═════════╪═════════╪═════════╪═════════╪═════════╡
│ Malloc (total) *          │       1 │       1 │       1 │       1 │       1 │       1 │       1 │   10000 │
├───────────────────────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┤
│ Throughput (# / s) (K)    │   10004 │    9991 │    9983 │    9975 │    9687 │    8759 │    3268 │   10000 │
├───────────────────────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┤
│ Time (total CPU) (ns) *   │     120 │     120 │     120 │     120 │     124 │     135 │     229 │   10000 │
╘═══════════════════════════╧═════════╧═════════╧═════════╧═════════╧═════════╧═════════╧═════════╧═════════╛

@fabianfett
Copy link
Contributor Author

To analyze performance, we look at the fastest runs, as we can assume that CPU cycles that were not related to the base64 job were minimal in these situations. This allows us to make the most accurate comparisons.

  • encode-1MB-toData-noOptions

    • Baseline (in Foundation today): 2894μs
    • Swift implementation: 723μs (4x faster)
    • This PR: 494μs (5.8x faster)
  • base64-encode-1MB-toString-lineLength64

    • Baseline (in Foundation today): 3150μs
    • Swift implementation: 708μs (4.4x faster)
    • This PR: 497μs (6.3x faster)
  • base64-encode-1MB-toString-noOptions

    • Baseline (in Foundation today): 2915μs
    • Swift implementation: 635μs (4.6x faster)
    • This PR: 414μs (7x faster)
  • base64-encode-jwtHeader-toString-noOptions

    • Baseline (in Foundation today): 409ns
    • Swift implementation: 149ns (2.7x faster)
    • This PR: 120ns (3.4x faster)

I think that looks good!

@fabianfett fabianfett marked this pull request as ready for review February 4, 2025 20:06
@fabianfett fabianfett mentioned this pull request Feb 5, 2025
@fabianfett
Copy link
Contributor Author

@swift-ci please test

@fabianfett
Copy link
Contributor Author

@swift-ci please test

@fabianfett
Copy link
Contributor Author

@swift-ci please test

@parkera parkera merged commit 2c08d60 into swiftlang:main Feb 20, 2025
2 of 3 checks passed
@fabianfett fabianfett deleted the ff-faster-encoding branch February 20, 2025 17: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.

2 participants