lukeed/clsxPublic

A tiny (239B) utility for constructing `className` strings conditionally.

Optimize clsx implementation for better performance #73

ClosedJonasWijneclosed 1 commit intomasterfromrefs/pull/73/head
  1. JO

    This PR optimizes the clsx implementation, resulting in more legible and faster code. the benchmarks show improved performance compared to the previous version.

    Changes:

    Updated toVal function to handle different input types more efficiently. Refactored the clsx function to use a more concise approach. Benchmark results: ``` # Strings clsx (prev) x 12,954,191 ops/sec ±0.32% (95 runs sampled) clsx x 14,163,905 ops/sec ±1.08% (101 runs sampled)

    # Objects clsx (prev) x 8,729,410 ops/sec ±0.15% (98 runs sampled) clsx x 10,610,018 ops/sec ±0.15% (100 runs sampled)

    # Arrays clsx (prev) x 10,043,091 ops/sec ±1.28% (99 runs sampled) clsx x 10,956,764 ops/sec ±0.20% (95 runs sampled)

    # Nested Arrays clsx (prev) x 8,232,897 ops/sec ±0.70% (93 runs sampled) clsx x 9,248,651 ops/sec ±0.12% (93 runs sampled)

    # Nested Arrays w/ Objects clsx (prev) x 7,975,890 ops/sec ±0.10% (98 runs sampled) clsx x 9,101,825 ops/sec ±0.12% (98 runs sampled)

    # Mixed clsx (prev) x 8,521,371 ops/sec ±1.77% (98 runs sampled) clsx x 9,270,741 ops/sec ±0.72% (99 runs sampled)

    # Mixed (Bad Data) clsx (prev) x 3,406,695 ops/sec ±0.22% (98 runs sampled) clsx x 3,822,356 ops/sec ±0.22% (99 runs sampled) ``` The updated implementation outperforms the previous version in all test cases.

    The resulting build files are also smaller ``` ❯ pnpm run build #before > clsx@1.2.1 build /Users/jonas/Research/clsx > node bin

    ~> "dist/clsx.m.js" (234 b) ~> "dist/clsx.js" (238 b) ~> "dist/clsx.min.js" (297 b) ❯ pnpm run build #after

    > clsx@1.2.1 build /Users/jonas/Research/clsx > node bin

    ~> "dist/clsx.m.js" (225 b) ~> "dist/clsx.js" (228 b) ~> "dist/clsx.min.js" (289 b) ```

  2. CO
  3. JonasWijnerenamed refactor: simplify toVal function and clsx function using ES6 to Optimize clsx implementation for better performance3 years ago on GitHub

  4. LU

    This is actually slower in all cases except `Nested Arrays` because the Array.isArray check is moved up. This appears tricky – sorry – but its because the latter (mostly repeat) candidate gets to benefit from JIT optimizations.

    I added some more to illustrate:

    - `clsx (prev)` = clsx@1.1.0 - `clsx` = current master - `clsx (#73)` = this PR

    ``` ⌁ ~/repos/oss/clsx  master± node -v v18.12.1 ⌁ ~/repos/oss/clsx  master± node bench

    # Strings clsx (prev) x 12,347,638 ops/sec ±0.23% (93 runs sampled) clsx (#73) x 13,488,185 ops/sec ±0.08% (101 runs sampled) clsx x 14,283,213 ops/sec ±0.11% (94 runs sampled)

    # Objects clsx (prev) x 8,286,705 ops/sec ±0.62% (99 runs sampled) clsx (#73) x 10,108,131 ops/sec ±0.50% (94 runs sampled) clsx x 10,426,194 ops/sec ±0.43% (94 runs sampled)

    # Arrays clsx (prev) x 9,758,624 ops/sec ±0.26% (101 runs sampled) clsx (#73) x 10,445,507 ops/sec ±0.12% (98 runs sampled) clsx x 10,494,471 ops/sec ±0.12% (100 runs sampled)

    # Nested Arrays clsx (prev) x 7,944,617 ops/sec ±0.39% (100 runs sampled) clsx (#73) x 8,832,563 ops/sec ±0.12% (101 runs sampled) clsx x 8,112,300 ops/sec ±0.72% (95 runs sampled)

    # Nested Arrays w/ Objects clsx (prev) x 7,211,581 ops/sec ±1.47% (96 runs sampled) clsx (#73) x 8,298,702 ops/sec ±1.24% (95 runs sampled) clsx x 8,320,432 ops/sec ±0.49% (94 runs sampled)

    # Mixed clsx (prev) x 8,105,500 ops/sec ±0.18% (99 runs sampled) clsx (#73) x 8,611,596 ops/sec ±0.58% (96 runs sampled) clsx x 9,158,882 ops/sec ±0.52% (99 runs sampled)

    # Mixed (Bad Data) clsx (prev) x 3,264,060 ops/sec ±0.46% (99 runs sampled) clsx (#73) x 3,553,928 ops/sec ±0.41% (99 runs sampled) clsx x 3,703,517 ops/sec ±0.49% (99 runs sampled) ```

    As mentioned, `clsx (#73)` is only faster in `Nested Arrays`.

    The ES6+ bits used here are mostly just sugar for the developer, but bring runtime costs. We can see this in action by swapping the benchmark order:

    ``` # Strings clsx (prev) x 12,365,782 ops/sec ±0.13% (98 runs sampled) clsx x 14,292,334 ops/sec ±0.06% (101 runs sampled) clsx (#73) x 13,477,591 ops/sec ±0.10% (99 runs sampled)

    # Objects clsx (prev) x 8,560,716 ops/sec ±0.11% (99 runs sampled) clsx x 10,526,618 ops/sec ±0.16% (95 runs sampled) clsx (#73) x 10,306,068 ops/sec ±0.13% (98 runs sampled)

    # Arrays clsx (prev) x 9,617,684 ops/sec ±0.16% (98 runs sampled) clsx x 10,529,453 ops/sec ±0.12% (102 runs sampled) clsx (#73) x 9,962,834 ops/sec ±0.49% (98 runs sampled)

    # Nested Arrays clsx (prev) x 7,691,389 ops/sec ±0.16% (99 runs sampled) clsx x 8,494,205 ops/sec ±0.13% (100 runs sampled) clsx (#73) x 8,650,357 ops/sec ±0.10% (99 runs sampled)

    # Nested Arrays w/ Objects clsx (prev) x 7,597,622 ops/sec ±0.36% (96 runs sampled) clsx x 8,444,634 ops/sec ±0.19% (98 runs sampled) clsx (#73) x 8,308,614 ops/sec ±0.16% (102 runs sampled)

    # Mixed clsx (prev) x 8,034,305 ops/sec ±0.24% (99 runs sampled) clsx x 9,458,763 ops/sec ±0.12% (99 runs sampled) clsx (#73) x 8,789,075 ops/sec ±0.79% (94 runs sampled)

    # Mixed (Bad Data) clsx (prev) x 3,230,945 ops/sec ±0.71% (98 runs sampled) clsx x 3,759,799 ops/sec ±0.20% (100 runs sampled) clsx (#73) x 3,640,722 ops/sec ±0.13% (95 runs sampled) ```

    Even though it's running last (and _should_ have everything JIT-wise going in its favor), it's still slightly slower than `clsx` directly. That's only _not_ true with the `Nested Arrays` test since that check is still sooner.

    ---

    If you want to open another PR that _just_ moves the `Array.isArray` check to its own `else if` branch, I think that's the best nugget from here.

    Thanks!

  5. lukeedclosed3 years ago on GitHub

  6. JO