Repository: nodejs/node
Pull request: buffer: let Buffer.of use heap

See also https://github.com/nodejs/node/pull/60372.
This is a somewhat less performant version than that PR, but significantly faster than `main` and is non semver-major (so it can be backported) and does not need doc changes.

`Buffer.of` is usually used on small static sizes (checked with GitHub codesearch)

The assumption that `createUnsafeBuffer(size)` is faster than `new Uint8Array(size)` / `new FastBuffer(size)` is incorrect for small sizes, as the latter does not call our allocator at all when it can go to heap, and the former is always an alloc.

Not letting it go to heap is hurting performance, direct allocations are slow.

See https://github.com/nodejs/node/pull/26301 for context

But even past the `v8_typed_array_max_size_in_heap` size (which is 64 bytes in the default config), this is still not worse than the old version way past expected `Buffer.of` sizes

Before:
```
% out/Release/node.0 benchmark/buffers/buffer-of.js
buffers/buffer-of.js n=500000 len=0: 24,633,954.827485334
buffers/buffer-of.js n=500000 len=1: 26,160,564.38801611
buffers/buffer-of.js n=500000 len=2: 26,280,949.92493504
buffers/buffer-of.js n=500000 len=8: 22,938,007.012818534
buffers/buffer-of.js n=500000 len=64: 9,478,358.537868412
buffers/buffer-of.js n=500000 len=256: 2,702,711.2345046536
buffers/buffer-of.js n=500000 len=1024: 974,881.6079890658
buffers/buffer-of.js n=500000 len=2048: 528,368.570893787
```

After:
```
% out/Release/node.2 benchmark/buffers/buffer-of.js
buffers/buffer-of.js n=500000 len=0: 33,703,264.18068379
buffers/buffer-of.js n=500000 len=1: 35,216,496.263314925
buffers/buffer-of.js n=500000 len=2: 34,097,195.761377595
buffers/buffer-of.js n=500000 len=8: 27,875,878.880465902
buffers/buffer-of.js n=500000 len=64: 10,335,418.86238334
buffers/buffer-of.js n=500000 len=256: 2,763,676.639751257
buffers/buffer-of.js n=500000 len=1024: 981,007.050158241
buffers/buffer-of.js n=500000 len=2048: 523,017.20255880937
```

For comparison, #60372 (even faster, but does that via pooling):
```
% ./out/Release/node.pooled benchmark/buffers/buffer-of.js
buffers/buffer-of.js n=500000 len=0: 33,882,981.248046815
buffers/buffer-of.js n=500000 len=1: 44,826,164.135482594
buffers/buffer-of.js n=500000 len=2: 44,895,948.02845056
buffers/buffer-of.js n=500000 len=8: 31,774,014.66683433
buffers/buffer-of.js n=500000 len=64: 10,511,875.791675644
buffers/buffer-of.js n=500000 len=256: 3,666,954.9211037415
buffers/buffer-of.js n=500000 len=1024: 1,082,339.2273017906
buffers/buffer-of.js n=500000 len=2048: 554,083.8215035282
```

Realistically, sizes 0-8 are most important.

Benchmark code (taken from https://github.com/nodejs/node/pull/60372 with some adjustments):
```js
'use strict';

const common = require('../common.js');

// Measure Buffer.of(...items) throughput for various lengths.
// We prebuild the items array to avoid measuring array construction,
// and vary the effective iterations with length to keep total work reasonable.

const bench = common.createBenchmark(main, {
  len: [0, 1, 2, 8, 64, 256, 1024, 2048],
  n: [5e5],
});

function main({ len, n }) {
  if (len < 10) n *= 10; // noisy otherwise
  // Inline small numbers calls as this is fast enough for small changes to be significant
  switch (len) {
    case 0:
      bench.start();
      for (let i = 0; i < n; i++) Buffer.of();
      bench.end(n);
      break;
    case 1:
      bench.start();
      for (let i = 0; i < n; i++) Buffer.of(0);
      bench.end(n);
      break;
    case 2:
      bench.start();
      for (let i = 0; i < n; i++) Buffer.of(0, 1);
      bench.end(n);
      break;
    case 8:
      bench.start();
      for (let i = 0; i < n; i++) Buffer.of(0, 1, 2, 3, 4, 5, 6, 7);
      bench.end(n);
      break;
    default: {
      const items = new Array(len);
      for (let i = 0; i < len; i++) items[i] = i & 0xFF;
      bench.start();
      for (let i = 0; i < n; i++) {
          Buffer.of(...items);
      }
      bench.end(n);
    }
  }
}
```

Changed files:
filename: lib/buffer.js
patch: @@ -386,8 +386,9 @@ Buffer.copyBytesFrom = function copyBytesFrom(view, offset, length) {
 // Refs: https://tc39.github.io/ecma262/#sec-%typedarray%.of
 // Refs: https://esdiscuss.org/topic/isconstructor#content-11
 const of = (...items) => {
-  const newObj = createUnsafeBuffer(items.length);
-  for (let k = 0; k < items.length; k++)
+  const len = items.length;
+  const newObj = new FastBuffer(len); // In heap for small sizes
+  for (let k = 0; k < len; k++)
     newObj[k] = items[k];
   return newObj;
 };