豆豆友情提示:这是一个非官方 GitHub 代理镜像,主要用于网络测试或访问加速。请勿在此进行登录、注册或处理任何敏感信息。进行这些操作请务必访问官方网站 github.com。 Raw 内容也通过此代理提供。
Skip to content

feat: Improve performance of atob and btoa#1503

Merged
zloirock merged 13 commits intozloirock:masterfrom
johnzhou721:pr-1464
Jan 31, 2026
Merged

feat: Improve performance of atob and btoa#1503
zloirock merged 13 commits intozloirock:masterfrom
johnzhou721:pr-1464

Conversation

@johnzhou721
Copy link
Copy Markdown
Contributor

@johnzhou721 johnzhou721 commented Jan 26, 2026

Based on #1464's work by @Chanran. [EDIT -- i got pr # wrong]

Draft for now as I test performance.

@johnzhou721
Copy link
Copy Markdown
Contributor Author

I've found how to test this -- ON the issue linked from in #1494 there's an example. I made some modifications -- below is a minimum testable example:

big_base64_data.txt
decode.html
polyfills.js

Put those in the same directory. polyfills.js is from a core-js-builder result from the script:

import builder from 'core-js-builder';

const bundle = await builder({
  modules: ['web.atob', 'web.btoa'],
  targets: '> 0.5%, not dead, ie 9-11',
  format: 'bundle',
  filename: 'polyfills.js',
});

and then I changed the headers of the functions in the bundle like this

--- polyfills.js	2026-01-25 21:49:44
+++ /Users/johnzhou/Downloads/decode/polyfills.js	2026-01-25 21:42:05
@@ -147,11 +147,11 @@
 
 // `atob` method
 // https://html.spec.whatwg.org/multipage/webappapis.html#dom-atob
-$({ global: true, bind: true, enumerable: true, forced: FORCED }, {
-  atob: function atob(data) {
+$({ global: true, bind: true, enumerable: true, forced: true }, {
+  atob1: function atob(data) {
     validateArgumentsLength(arguments.length, 1);
     // `webpack` dev server bug on IE global methods - use call(fn, global, ...)
-    if (BASIC && !NO_SPACES_IGNORE && !NO_ENCODING_CHECK) return call($atob, globalThis, data);
+    // if (BASIC && !NO_SPACES_IGNORE && !NO_ENCODING_CHECK) return call($atob, globalThis, data);
     var string = replace(toString(data), whitespaces, '');
     var output = new $Array(Math.ceil(string.length * 3 / 4));
     var outputIndex = 0;
@@ -1763,11 +1763,11 @@
 
 // `btoa` method
 // https://html.spec.whatwg.org/multipage/webappapis.html#dom-btoa
-$({ global: true, bind: true, enumerable: true, forced: !BASIC || NO_ARG_RECEIVING_CHECK || WRONG_ARG_CONVERSION || WRONG_ARITY }, {
-  btoa: function btoa(data) {
+$({ global: true, bind: true, enumerable: true, forced: true }, {
+  btoa1: function btoa(data) {
     validateArgumentsLength(arguments.length, 1);
     // `webpack` dev server bug on IE global methods - use call(fn, global, ...)
-    if (BASIC) return call($btoa, globalThis, toString(data));
+    // if (BASIC) return call($btoa, globalThis, toString(data));
     var string = toString(data);
     var output = new $Array(Math.ceil(string.length * 4 / 3));
     var outputIndex = 0;

Note that the if(BASIC... lines can be uncommented to compare results with the builtin cannocial function.

In decode.html put the things from big_base64_data in. The polyfills I've experimented with -- I changed the things in the bundle to the old version and then reverted it to the new version I've posted here.

The result is 28.88ms to 14.26ms in encoding with 20 runs, and 83.29ms to 56.27ms with 20 runs (with the runs specified in the textfields below the input at the top).

@johnzhou721 johnzhou721 marked this pull request as ready for review January 26, 2026 03:52
@johnzhou721
Copy link
Copy Markdown
Contributor Author

@zloirock This should be ready to take a look at now.

Copy link
Copy Markdown
Owner

@zloirock zloirock left a comment

Choose a reason for hiding this comment

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

  • Math.ceil should be cached locally like fromCharCode.
  • I'm not sure about this logic determination of the array length - if I understand correctly, in some cases, it will be more than the real number of elements and the result array will have holes that could cause deoptimization for join.
  • Array#join should be used as the rest prototype methods - with uncurryThis.
  • There are many other methods that could have similar problems - for example, Uint8Array base64 / hex methods - you could also improve them. However, sure, this is not a requirement for the adoption of the PR.

@johnzhou721
Copy link
Copy Markdown
Contributor Author

  • I'm not sure about this logic determination of the array length - if I understand correctly, in some cases, it will be more than the real number of elements and the result array will have holes that could cause deoptimization for join.

This is true for the atob decoding -- good catch. For btoa we actually underallocate which means that the assignment might reallocate and cause bad performance.

Would it be good to add comments inline explaining the math?

@johnzhou721
Copy link
Copy Markdown
Contributor Author

@zloirock Tested on Windows 11 with IE (by using a .hta file with a link in it to hack out IE) and also on macOS with latest Chrome, this PR now seems to offer some significant speedups. However my setups are slow so I'd appreciate you also testing this.

Ready for another review.

@johnzhou721 johnzhou721 requested a review from zloirock January 29, 2026 04:16
@zloirock
Copy link
Copy Markdown
Owner

Thanks.

@zloirock zloirock merged commit 40b6311 into zloirock:master Jan 31, 2026
27 checks passed
@johnzhou721
Copy link
Copy Markdown
Contributor Author

Glad to help micro improve performance of some polyfills!

@johnzhou721
Copy link
Copy Markdown
Contributor Author

Next up for performance improvements is #1510, with UInt8Array hex functions.

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