cljs-dev 2025-11-28

@dnolen I found that writing method values as:

function bar(x, ...xs) { return String.prototype.toUpperCase.apply(x, xs); }
makes them more near the performance of direct interop
function direct(x) { return x.toUpperCase(); }
foo (Reflect.apply): 5321.67 ms
bar (call): 4646.54 ms
direct: 4483.81 ms
Any reason not to do this? Can write patch of course, but first it would be good to hear your opinion

Meh never mind, bad benchmark probably

doens't matter that much:

cljs.user=> (simple-benchmark [f (js/eval "(x, ...xs) => globalThis.String.prototype.toUpperCase.apply(x, xs)")] (f "foo") 100000000)
[f (js/eval "(x, ...xs) => globalThis.String.prototype.toUpperCase.apply(x, xs)")], (f "foo"), 100000000 runs, 1446 msecs
nil
cljs.user=> (simple-benchmark [f (js/eval "(x, ...xs) => Reflect.apply(globalThis.String.prototype.toUpperCase, x, xs)")] (f "foo") 100000000)
[f (js/eval "(x, ...xs) => Reflect.apply(globalThis.String.prototype.toUpperCase, x, xs)")], (f "foo"), 100000000 runs, 1485 msecs
nil

Reflect just isn't slow - people need to realize this 🙂

in a conversation with @thheller we found that direct interop is just faster, so going with method values wouldn't buy much but slightly worse performance

but not by crazy margin

that's true

cljs.user=> (simple-benchmark [f (js/eval "(x, ...xs) => x.toUpperCase(x)")] (f "foo") 100000000)
[f (js/eval "(x, ...xs) => x.toUpperCase(x)")], (f "foo"), 100000000 runs, 1246 msecs

it's just not enough to lose any sleep over in real programs

probably true

Benchmarking JS is pretty hard. I had chatgpt generate this for me:

function benchmark(fn, label, iterations = 10_000_0000) {
  const start = performance.now();
  for (let i = 0; i < iterations; i++) {
    fn("dude");
  }
  const end = performance.now();
  console.log(`${label}: ${(end - start).toFixed(2)} ms`);
}
But it just mattered with which function you ran it first with. First function fast, second slow (probably de-optimization of some sort). simple-benchmark didn't have this problem at least since it generates a new function + loop every time