esbuild is faster but the built programs on average are slower

by โ€” posted on

Programs built by esbuild v0.11.5 appear to perform on average 27% slower than the same code built by Rollup. Some packages perform more than 90% slower though! In this context, I can't use it in production.

Here are the details of the benchmarks. I hope it's me, not esbuild.

ยง Setup

  • MacOS 11.2.3 Big Sur
  • node 15.13
  • npm 7.7.6
  • esbuild 0.11.5
  • rollup 2.44.0
  • @babel/core 7.13.14
  • benchmark 2.1.4

also, the particulars:

ยง Results

CJS built using Rollup, ops/s.CJS built using esbuild, ops/s.difference

๐Ÿ“ฆ object-no-new-keys 3.1.0

Check, does a plain object (AST/JSON) has any unique keys, not present in a reference object (another AST/JSON)

604,665880,57745%

๐Ÿ“ฆ util-array-object-or-both 3.1.0

Validate and normalise user choice: array, object or both?

1,417,1681,944,42837%

๐Ÿ“ฆ ranges-is-index-within 2.1.0

Checks if index is within any of the given string index ranges

1,245,4051,687,38535%

๐Ÿ“ฆ ast-get-values-by-key 3.1.0

Extract values and paths from AST by keys OR set them by keys

52,66668,46329%

๐Ÿ“ฆ ast-delete-object 2.1.0

Delete all plain objects in AST if they contain a certain key/value pair

21,55827,40427%

๐Ÿ“ฆ ranges-sort 4.1.0

Sort string index ranges

1,206,2251,498,70224%

๐Ÿ“ฆ string-overlap-one-on-another 2.1.0

Lay one string on top of another, with an optional offset

939,2641,164,13123%

๐Ÿ“ฆ string-split-by-whitespace 2.1.0

Split string into array by chunks of whitespace

28,76135,35822%

๐Ÿ“ฆ string-convert-indexes 4.1.0

Convert between native JS string character indexes and grapheme-count-based indexes

49,52060,14121%

๐Ÿ“ฆ ast-deep-contains 3.1.0

Like t.same assert on array of objects, where element order doesn't matter

46,51755,47619%

๐Ÿ“ฆ edit-package-json 0.4.0

Edit package.json without parsing, as string, to keep the formatting intact

17,62820,66417%

๐Ÿ“ฆ helga 1.4.0

Your next best friend when editing complex nested code

808,293946,78017%

๐Ÿ“ฆ object-set-all-values-to 4.1.0

Recursively walk the input and set all found values in plain objects to something

93,886108,90115%

๐Ÿ“ฆ ast-monkey-traverse 2.1.0

Utility library to traverse AST

7,3318,42914%

๐Ÿ“ฆ charcode-is-valid-xml-name-character 1.13.0

Does a given character belong to XML spec's "Production 4 OR 4a" type (is acceptable for XML element's name)

616,548708,69114%

๐Ÿ“ฆ ast-monkey-util 1.4.0

Utility library of AST helper functions

5,567,7156,258,12112%

๐Ÿ“ฆ easy-replace 4.1.0

Replace strings with optional lookarounds, but without regexes

343,214385,23712%

๐Ÿ“ฆ string-range-expander 2.1.0

Expands string index ranges within whitespace boundaries until letters are met

401,309441,70510%

๐Ÿ“ฆ ast-loose-compare 2.1.0

Compare anything: AST, objects, arrays and strings

1,119,9771,231,3849%

๐Ÿ“ฆ object-flatten-all-arrays 5.1.0

Merge and flatten any arrays found in all values within plain objects

112,809123,7309%

๐Ÿ“ฆ object-all-values-equal-to 2.1.0

Does the AST/nested-plain-object/array/whatever contain only one kind of value?

755,624817,2778%

๐Ÿ“ฆ ranges-offset 2.1.0

Increment or decrement each index in every range

5,431,8655,913,4478%

๐Ÿ“ฆ csv-split-easy 5.1.0

Splits the CSV string into array of arrays, each representing a row of columns

29,34231,5057%

๐Ÿ“ฆ str-indexes-of-plus 3.1.0

Like indexOf but returns array and counts per-grapheme

5,612,8986,057,9857%

๐Ÿ“ฆ detect-is-it-html-or-xhtml 4.1.0

Answers, is the string input string more an HTML or XHTML (or neither)

4,104,6184,336,8695%

๐Ÿ“ฆ is-char-suitable-for-html-attr-name 2.1.0

Is given character suitable to be in an HTML attribute's name?

138,053,165144,933,0374%

๐Ÿ“ฆ string-left-right 4.1.0

Looks up the first non-whitespace character to the left/right of a given index

2,796,1422,909,4654%

๐Ÿ“ฆ string-unfancy 4.1.0

Replace all n/m dashes, curly quotes with their simpler equivalents

184,901193,2164%

๐Ÿ“ฆ bitbucket-slug 2.1.0

Generate BitBucket readme header anchor slug URLs. Unofficial, covers whole ASCII and a bit beyond

463,477471,8691%

๐Ÿ“ฆ all-named-html-entities 1.6.0

List of all named HTML entities

12,988,33713,076,6000%

๐Ÿ“ฆ arrayiffy-if-string 3.14.0

Put non-empty strings into arrays, turn empty-ones into empty arrays. Bypass everything else

851,031,198852,625,7280%

๐Ÿ“ฆ color-shorthand-hex-to-six-digit 3.1.0

Convert shorthand hex color codes into full

991,925995,9750%

๐Ÿ“ฆ object-flatten-referencing 5.1.0

Flatten complex nested objects according to a reference objects

46,43946,8790%

๐Ÿ“ฆ array-includes-with-glob 3.1.0

Like _.includes but with wildcards

389,175388,184-1%

๐Ÿ“ฆ csv-sort 5.1.0

Sorts double-entry bookkeeping CSV coming from internet banking

828822-1%

๐Ÿ“ฆ object-boolean-combinations 4.1.0

Consumes a defaults object with booleans, generates all possible variations of it

26,87226,850-1%

๐Ÿ“ฆ regex-is-jsp 2.1.0

Regular expression for detecting JSP (Java Server Pages) code

857,259,446841,373,993-2%

๐Ÿ“ฆ string-character-is-astral-surrogate 1.13.0

Tells, is given character a part of astral character, specifically, a high and low surrogate

386,957,104380,800,575-2%

๐Ÿ“ฆ util-nonempty 3.1.0

Is the input (plain object, array, string or whatever) not empty?

758,700748,129-2%

๐Ÿ“ฆ html-all-known-attributes 4.1.0

All HTML attributes known to the Humanity

872,646,305853,734,857-3%

๐Ÿ“ฆ html-entities-not-email-friendly 0.5.0

All HTML entities which are not email template friendly

8,1527,922-3%

๐Ÿ“ฆ object-delete-key 2.1.0

Delete keys from all arrays or plain objects, nested within anything, by key or by value or by both, and clean up afterwards. Accepts wildcards

487476-3%

๐Ÿ“ฆ ast-is-empty 2.1.0

Find out, is nested array/object/string/AST tree is empty

842,803816,877-4%

๐Ÿ“ฆ regex-empty-conditional-comments 1.11.0

Regular expression for matching HTML empty conditional comments

879,313,074844,474,669-4%

๐Ÿ“ฆ regex-jinja-specific 2.1.0

Regular expression for detecting Python-specific Jinja code

879,049,231848,836,389-4%

๐Ÿ“ฆ array-pull-all-with-glob 5.1.0

Like _.pullAll but with globs (wildcards)

80,49276,541-5%

๐Ÿ“ฆ is-html-tag-opening 2.1.0

Does an HTML tag start at given position?

208,444199,699-5%

๐Ÿ“ฆ regex-is-jinja-nunjucks 2.1.0

Regular expression for detecting Jinja or Nunjucks code

927,186,953879,108,894-6%

๐Ÿ“ฆ detect-templating-language 2.1.0

Detects various templating languages present in string

3,733,3853,473,999-7%

๐Ÿ“ฆ ast-contains-only-empty-space 2.1.0

Does AST contain only empty space?

28,90526,653-8%

๐Ÿ“ฆ is-language-code 3.1.0

Is given string a language code (as per IANA)

1,795,4261,632,861-10%

๐Ÿ“ฆ ast-compare 2.1.0

Compare anything: AST, objects, arrays, strings and nested thereof

45,94237,713-18%

๐Ÿ“ฆ object-fill-missing-keys 8.1.0

Add missing keys into plain objects, according to a reference object

83,73068,346-19%

๐Ÿ“ฆ ast-monkey 7.15.0

Traverse and edit AST

10,9148,680-21%

๐Ÿ“ฆ ast-get-object 2.1.0

Getter/setter for nested parsed HTML AST's, querying objects by key/value pairs

84,22765,060-23%

๐Ÿ“ฆ is-media-descriptor 3.2.0

Is given string a valid media descriptor (including media query)?

681,155528,020-23%

๐Ÿ“ฆ test-mixer 2.1.0

Test helper to generate function opts object variations

128,66190,184-30%

๐Ÿ“ฆ is-relative-uri 3.1.0

Is given string a relative URI?

965,326664,065-32%

๐Ÿ“ฆ json-comb-core 6.9.0

The inner core of json-comb

5,1943,554-32%

๐Ÿ“ฆ line-column-mini 1.2.0

Convert string index to line-column position

536,985352,397-35%

๐Ÿ“ฆ lerna-clean-changelogs 2.1.0

Removes frivolous entries from commitizen generated changelogs

174,708108,303-39%

๐Ÿ“ฆ check-types-mini 6.1.0

Validate options object

24,78614,888-40%

๐Ÿ“ฆ ranges-apply 5.1.0

Take an array of string index ranges, delete/replace the string according to them

402,362240,594-41%

๐Ÿ“ฆ ranges-ent-decode 4.1.0

Recursive HTML entity decoding for Ranges workflow

313,404176,076-44%

๐Ÿ“ฆ object-merge-advanced 12.1.0

Recursively, deeply merge of anything (objects, arrays, strings or nested thereof), which weighs contents by type hierarchy to ensure the maximum content is retained

10,8205,918-46%

๐Ÿ“ฆ string-trim-spaces-only 3.1.0

Like String.trim() but you can choose granularly what to trim

1,122,252598,728-47%

๐Ÿ“ฆ ranges-regex 4.1.0

Integrate regex operations into Ranges workflow

634,846325,307-49%

๐Ÿ“ฆ json-variables 10.1.0

Resolves custom-marked, cross-referenced paths in parsed JSON

4,6632,334-50%

๐Ÿ“ฆ string-uglify 1.5.0

Shorten sets of strings deterministically, to be git-friendly

162,09375,494-54%

๐Ÿ“ฆ detergent 7.1.0

Extracts, cleans and encodes text

1,003447-56%

๐Ÿ“ฆ ast-monkey-traverse-with-lookahead 2.1.0

Utility library to traverse AST, reports upcoming values

8,4463,664-57%

๐Ÿ“ฆ array-of-arrays-into-ast 2.1.0

Turns an array of arrays of data into a nested tree of plain objects

21,0309,028-58%
40,35617,296-58%

๐Ÿ“ฆ string-remove-widows 2.1.0

Helps to prevent widow words in a text

4,9672,083-59%

๐Ÿ“ฆ tap-parse-string-to-object 2.1.0

Parses raw Tap: string-to-object or stream-to-a-promise-of-an-object

72,45730,365-59%

๐Ÿ“ฆ array-group-str-omit-num-char 4.1.0

Groups array of strings by omitting number characters

123,56248,255-61%

๐Ÿ“ฆ email-all-chars-within-ascii 3.1.0

Scans all characters within a string and checks are they within ASCII range

304,707116,948-62%

๐Ÿ“ฆ ranges-process-outside 4.1.0

Iterate string considering ranges, as if they were already applied

139,14852,921-62%

๐Ÿ“ฆ ranges-merge 7.1.0

Merge and sort string index ranges

402,352129,858-68%

๐Ÿ“ฆ string-find-heads-tails 4.1.0

Finds where are arbitrary templating marker heads and tails located

83,69727,322-68%

๐Ÿ“ฆ string-match-left-right 7.1.0

Match substrings on the left or right of a given index, ignoring whitespace

89,93629,353-68%

๐Ÿ“ฆ html-img-alt 2.1.0

Adds missing alt attributes to img tags. Non-parsing

38,24111,878-69%

๐Ÿ“ฆ string-remove-thousand-separators 5.1.0

Detects and removes thousand separators (dot/comma/quote/space) from string-type digits

76,82624,111-69%

๐Ÿ“ฆ html-table-patcher 4.1.0

Visual helper to place templating code around table tags into correct places

422129-70%

๐Ÿ“ฆ ranges-invert 4.1.0

Invert string index ranges

266,47882,308-70%

๐Ÿ“ฆ string-process-comma-separated 2.1.0

Extracts chunks from possibly comma or whatever-separated string

359,421105,242-71%

๐Ÿ“ฆ string-remove-duplicate-heads-tails 5.1.0

Detect and (recursively) remove head and tail wrappings around the input string

5,2141,528-71%

๐Ÿ“ฆ stristri 3.2.0

Extracts or deletes HTML, CSS, text and/or templating tags from string

535157-71%

๐Ÿ“ฆ string-collapse-white-space 9.1.0

Replace chunks of whitespace with a single spaces

82,40523,875-72%

๐Ÿ“ฆ ranges-crop 4.1.0

Crop array of ranges when they go beyond the reference string's length

533,039148,791-73%

๐Ÿ“ฆ codsen-tokenizer 5.6.0

HTML and CSS lexer aimed at code with fatal errors, accepts mixed coding languages

10,0752,674-74%

๐Ÿ“ฆ is-html-attribute-closing 2.3.0

Is a character on a given index a closing of an HTML attribute?

295,48663,758-79%

๐Ÿ“ฆ codsen-parser 0.11.0

Parser aiming at broken or mixed code, especially HTML & CSS

6,6721,381-80%

๐Ÿ“ฆ string-fix-broken-named-entities 5.4.0

Finds and fixes common and not so common broken named HTML entities, returns ranges array of fixes

82,16515,341-82%

๐Ÿ“ฆ string-extract-sass-vars 2.1.0

Parse SASS variables file into a plain object of CSS key-value pairs

40,9326,498-85%

๐Ÿ“ฆ string-collapse-leading-whitespace 5.1.0

Collapse the leading and trailing whitespace of a string

1,741,567203,157-89%

๐Ÿ“ฆ string-extract-class-names 6.1.0

Extracts CSS class/id names from a string

268,80724,855-91%

๐Ÿ“ฆ array-of-arrays-sort-by-col 3.1.0

Sort array of arrays by column, rippling the sorting outwards from that column

2,631,276215,987-92%

๐Ÿ“ฆ ranges-push 5.1.0

Gather string index ranges

1,369,476109,753-92%

๐Ÿ“ฆ js-row-num 4.1.0

Update all row numbers in all console.logs in JS code

12,136918-93%

๐Ÿ“ฆ string-apostrophes 1.5.0

Comprehensive, HTML-entities-aware tool to typographically-correct the apostrophes and single/double quotes

48,8113,215-94%

๐Ÿ“ฆ ranges-iterate 2.1.0

Iterate a string and any changes within given string index ranges

12,707,835670,891-95%

๐Ÿ“ฆ string-find-malformed 2.1.0

Search for a malformed string. Think of Levenshtein distance but in search

324,13718,415-95%

๐Ÿ“ฆ string-strip-html 8.3.0

Strips HTML tags from strings. No parser, accepts mixed sources

8,835213-98%
Average:-27%
Median:-18%

ยง Maybe my esbuild settings are wrong?

Programs benchmarked above were built using esbuild on the following settings opens in a new tab:

  • format as "cjs"
  • bundle on, but exclude any dependencies or peer dependencies (ends up bundling local imports only)
  • minify on (to strip console.log and console.time instances which we keep in the source to help the maintainability)
  • target as node10.4

There shouldn't be any surprises, though?

ยง You can try yourself

  1. Clone codsen monorepo, git clone https://github.com/codsen/codsen.git
  2. cd codsen
  3. npm run bootstrap (it's a monorepo, you don't npm i, Lerna does it)
  4. cd into any package's root, cd packages/string-strip-html
  5. for Rollup, build and benchmark npm run build && npm run perf
  6. for esbuild, build and benchmark npm run esbuild && npm run perf

Benchmarks run on a cjs build and vary depending on thermal throttling, machine's load and other factors.

ยง Takeaway

For me, esbuild still feels not production-ready yet, even the perf issues aside:

Fingers crossed, maybe esbuild will mature in a year or so. After all, it took three years and seven months for Rollup to reach v.1, and esbuild is still one-year-old!

Related packages:

๐Ÿ“ฆ esbuild opens in a new tab
An extremely fast JavaScript bundler and minifier
๐Ÿ“ฆ rollup opens in a new tab
Next-generation ES module bundler