Skip to content

⚡️ Faster fc.dictionary on generate#7047

Merged
dubzzz merged 2 commits into
mainfrom
claude/perf-dict-mapper-direct-assign
Jun 4, 2026
Merged

⚡️ Faster fc.dictionary on generate#7047
dubzzz merged 2 commits into
mainfrom
claude/perf-dict-mapper-direct-assign

Conversation

@dubzzz

@dubzzz dubzzz commented Jun 4, 2026

Copy link
Copy Markdown
Owner

Description

AI-agent disclosure: this PR was authored by an automated agent (Claude Code) and has not been line-by-line reviewed by a human before submission.

This speeds up the generate path of fc.dictionary(...). There is no observable behavior change: generated dictionaries have the exact same keys, values, property attributes (configurable/enumerable/writable) and prototype (object vs null) as before — a pure performance change with patch impact.

What changed

The mapper that turns generated [key, value] pairs into the final object (keyValuePairsToObjectMapper) used Object.defineProperty for every pair:

safeObjectDefineProperty(obj, keyValue[0], { enumerable: true, configurable: true, writable: true, value: keyValue[1] });

Object.defineProperty with all three flags set to true produces exactly the same own data property as a plain assignment obj[key] = value, but in V8 it is markedly slower and tends to push the freshly created object onto a slower path. The hot loop now assigns directly.

As with the matching fc.record change, the single exception is the string key "__proto__": a plain assignment would trigger the prototype setter on a regular object instead of defining an own property, so that one key keeps using defineProperty. Symbol/number keys and every other string key (including constructor, toString, …) are plain data properties and are safely created by assignment.

Why this design

  • Minimal, surgical change isolated to the one mapper, with an identical observable result.
  • The __proto__-only guard is provably sufficient — __proto__ is the only accessor on Object.prototype; every other "dangerous" key is a plain data property that assignment safely shadows. The existing dangerous-key unit tests (['__proto__', 'constructor', 'toString'], both object- and null-prototype) cover exactly this and still pass.

Measurements

Isolated mapper microbenchmark (lower is better):

keys Object.defineProperty direct assignment
3 ~799 ns/op ~59 ns/op
10 ~2518 ns/op ~188 ns/op

fc.dictionary().generate() is dominated by string-key generation, so the end-to-end gain is most visible on larger dictionaries; the mapper itself is ~13× faster.

Tests

No new tests required — behavior is unchanged and the existing suites pin it down. test/unit/arbitrary/_internals/mappers/KeyValuePairsToObject.spec.ts (incl. the dangerous-key and null-prototype cases) and dictionary.spec.ts all pass (24 tests); tsc is clean. A changeset (patch) is included.

This is a sibling of the fc.record mapper change (kept as a separate, single-concern PR).

Checklist

Don't delete this checklist and make sure you do the following before opening the PR

  • I have a full understanding of every line in this PR — whether the code was hand-written, AI-generated, copied from external sources or produced by any other tool
  • I flagged the impact of my change (minor / patch / major) either by running pnpm run bump or by following the instructions from the changeset bot
  • I kept this PR focused on a single concern and did not bundle unrelated changes
  • I followed the gitmoji specification for the name of the PR, including the package scope (e.g. 🐛(vitest) Something...) when the change targets a package other than fast-check
  • I added relevant tests and they would have failed without my PR (when applicable)

Generated by Claude Code

Build generated dictionaries with direct property assignment instead of
Object.defineProperty in the key-value-pairs-to-object mapper.

Object.defineProperty with { configurable, enumerable, writable } all set
to true produces exactly the same own data property as a plain
assignment, but is significantly slower in V8 and pushes the freshly
created object onto a slower path. Switching to assignment keeps the
object on the fast path and avoids the per-property builtin call.

The only key that cannot use a plain assignment is the string
"__proto__" (it would trigger the prototype setter on a regular object
instead of defining an own property), so it keeps using defineProperty.
Symbol/number keys and every other string key are assigned directly.

Isolated mapper microbenchmark (lower is better):
  - 3 keys:  ~799 ns/op -> ~59 ns/op
  - 10 keys: ~2518 ns/op -> ~188 ns/op
@changeset-bot

changeset-bot Bot commented Jun 4, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: d2429e9

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
fast-check Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@pkg-pr-new

pkg-pr-new Bot commented Jun 4, 2026

Copy link
Copy Markdown
@fast-check/ava

npm i https://pkg.pr.new/@fast-check/ava@7047

fast-check

npm i https://pkg.pr.new/fast-check@7047

@fast-check/jest

npm i https://pkg.pr.new/@fast-check/jest@7047

@fast-check/packaged

npm i https://pkg.pr.new/@fast-check/packaged@7047

@fast-check/poisoning

npm i https://pkg.pr.new/@fast-check/poisoning@7047

@fast-check/vitest

npm i https://pkg.pr.new/@fast-check/vitest@7047

@fast-check/worker

npm i https://pkg.pr.new/@fast-check/worker@7047

commit: d2429e9

Comment thread packages/fast-check/src/arbitrary/_internals/mappers/KeyValuePairsToObject.ts Outdated
@dubzzz
dubzzz enabled auto-merge (squash) June 4, 2026 21:53
@github-actions

github-actions Bot commented Jun 4, 2026

Copy link
Copy Markdown
Contributor

⏱️ Benchmark Results

Click to expand

 RUN  v4.1.5 /home/runner/work/fast-check/fast-check


 ✓  fast-check  test/bench/arbitraries.bench.ts > generate 44434ms
     name                                                         hz     min      max    mean     p75     p99    p995     p999      rme  samples
   · boolean()                                          6,998,258.47  0.0001   0.3276  0.0001  0.0001  0.0002  0.0003   0.0006   ±0.48%  3499130  [1.00x] ⇑
     boolean()                                          6,963,491.89  0.0001   0.2650  0.0001  0.0001  0.0002  0.0002   0.0005   ±0.44%  3481746  (baseline)
   · integer()                                          7,139,727.26  0.0001   0.1216  0.0001  0.0002  0.0002  0.0002   0.0003   ±0.13%  3569864  [1.01x] ⇑
     integer()                                          7,050,448.72  0.0001   0.2343  0.0001  0.0002  0.0002  0.0002   0.0003   ±0.15%  3525225  (baseline)
   · maxSafeInteger()                                   5,343,967.96  0.0001   0.2002  0.0002  0.0002  0.0003  0.0004   0.0009   ±0.18%  2671984  [1.02x] ⇑
     maxSafeInteger()                                   5,256,234.59  0.0001   0.3917  0.0002  0.0002  0.0003  0.0003   0.0005   ±0.32%  2628118  (baseline)
   · bigInt()                                           1,213,773.27  0.0006   0.4476  0.0008  0.0011  0.0015  0.0020   0.0081   ±0.41%   606899  [1.02x] ⇑
     bigInt()                                           1,195,214.61  0.0006   0.4843  0.0008  0.0012  0.0013  0.0017   0.0036   ±0.41%   597608  (baseline)
   · float()                                            4,487,334.80  0.0002   0.5405  0.0002  0.0002  0.0003  0.0004   0.0007   ±0.36%  2243668  [1.01x] ⇑
     float()                                            4,428,697.56  0.0002   0.5763  0.0002  0.0002  0.0003  0.0004   0.0008   ±0.42%  2214349  (baseline)
   · double()                                           1,670,391.10  0.0004   0.3625  0.0006  0.0007  0.0010  0.0013   0.0019   ±0.37%   835196  [0.99x] ⇓
     double()                                           1,688,516.48  0.0004   0.3113  0.0006  0.0007  0.0009  0.0013   0.0020   ±0.33%   844259  (baseline)
   · date()                                             3,043,081.73  0.0003   0.5425  0.0003  0.0003  0.0004  0.0006   0.0009   ±0.33%  1521541  [1.00x] ⇑
     date()                                             3,033,095.36  0.0003   0.5185  0.0003  0.0003  0.0004  0.0005   0.0012   ±0.35%  1516548  (baseline)
   · string()                                           1,030,665.87  0.0002   0.4173  0.0010  0.0012  0.0022  0.0029   0.0118   ±0.44%   515334  [1.02x] ⇑
     string()                                           1,010,870.48  0.0002   0.5716  0.0010  0.0012  0.0022  0.0032   0.0115   ±0.52%   505436  (baseline)
   · string({ maxLength: 100, size: 'max' })              188,602.47  0.0003   0.5275  0.0053  0.0077  0.0149  0.0163   0.0247   ±0.65%    94302  [1.04x] ⇑
     string({ maxLength: 100, size: 'max' })              180,894.62  0.0003   0.4880  0.0055  0.0080  0.0159  0.0170   0.0241   ±0.68%    90448  (baseline)
   · string({ unit: 'grapheme' })                         685,984.08  0.0002   0.4034  0.0015  0.0019  0.0034  0.0038   0.0136   ±0.42%   342993  [1.04x] ⇑
     string({ unit: 'grapheme' })                         662,643.83  0.0002   0.5275  0.0015  0.0020  0.0036  0.0042   0.0129   ±0.51%   331322  (baseline)
   · base64String()                                       999,701.02  0.0003   0.4386  0.0010  0.0012  0.0021  0.0029   0.0118   ±0.40%   499851  [1.04x] ⇑
     base64String()                                       962,395.44  0.0003   0.6004  0.0010  0.0013  0.0023  0.0033   0.0116   ±0.64%   481198  (baseline)
   · array(integer())                                   1,649,567.23  0.0002   0.4928  0.0006  0.0007  0.0014  0.0021   0.0036   ±0.43%   824784  [0.98x] ⇓
     array(integer())                                   1,689,707.68  0.0002   0.5697  0.0006  0.0006  0.0015  0.0022   0.0042   ±0.53%   844855  (baseline)
   · array(integer(), { maxLength: 100, size: 'max' })    298,702.81  0.0002  30.1856  0.0033  0.0042  0.0120  0.0157   0.0245  ±11.89%   149352  [0.88x] ⇓
     array(integer(), { maxLength: 100, size: 'max' })    341,174.36  0.0002   0.4942  0.0029  0.0039  0.0101  0.0107   0.0181   ±0.67%   170588  (baseline)
   · uniqueArray(integer())                             1,172,329.67  0.0002   0.5118  0.0009  0.0010  0.0020  0.0025   0.0139   ±0.59%   586165  [1.00x] ⇑
     uniqueArray(integer())                             1,169,428.18  0.0002  24.8560  0.0009  0.0010  0.0021  0.0027   0.0056   ±9.76%   584715  (baseline)
   · set(integer())                                     1,037,231.46  0.0003   0.3462  0.0010  0.0012  0.0022  0.0023   0.0073   ±0.33%   518616  [1.02x] ⇑
     set(integer())                                     1,014,488.52  0.0003   0.4405  0.0010  0.0012  0.0023  0.0024   0.0109   ±0.42%   507245  (baseline)
   · tuple(integer())                                   3,757,282.90  0.0002   0.5015  0.0003  0.0003  0.0004  0.0005   0.0012   ±0.58%  1878642  [1.01x] ⇑
     tuple(integer())                                   3,726,739.42  0.0002   0.4005  0.0003  0.0003  0.0004  0.0005   0.0012   ±0.51%  1863370  (baseline)
   · record({ a: integer() })                           1,308,593.38  0.0006   0.4906  0.0008  0.0008  0.0011  0.0016   0.0022   ±0.54%   654297  [1.03x] ⇑
     record({ a: integer() })                           1,270,135.71  0.0006   0.4298  0.0008  0.0008  0.0012  0.0015   0.0024   ±0.45%   635069  (baseline)
   · dictionary(string(), integer())                      110,621.16  0.0005   4.6249  0.0090  0.0129  0.0248  0.0300   0.0386   ±2.58%    55312  [1.00x] ⇑
     dictionary(string(), integer())                      110,583.32  0.0005   3.9706  0.0090  0.0130  0.0223  0.0247   0.0328   ±2.38%    55292  (baseline)
   · map(string(), integer())                             173,838.21  0.0003   0.5327  0.0058  0.0086  0.0145  0.0179   0.0257   ±0.74%    86920  [1.01x] ⇑
     map(string(), integer())                             172,187.59  0.0003   0.4200  0.0058  0.0087  0.0148  0.0172   0.0245   ±0.65%    86095  (baseline)
   · constantFrom(...)                                  8,055,258.92  0.0001   0.0456  0.0001  0.0001  0.0001  0.0002   0.0002   ±0.11%  4027630  [1.08x] ⇑
     constantFrom(...)                                  7,460,684.81  0.0001   0.2687  0.0001  0.0001  0.0002  0.0002   0.0003   ±0.15%  3730343  (baseline)
   · oneof(integer(), integer())                        4,064,936.33  0.0002   0.0483  0.0002  0.0003  0.0003  0.0004   0.0008   ±0.12%  2032469  [1.12x] ⇑
     oneof(integer(), integer())                        3,644,326.84  0.0002   0.0455  0.0003  0.0003  0.0004  0.0004   0.0009   ±0.12%  1822164  (baseline)
   · oneof({ weight, arbitrary }, ...)                  4,067,274.80  0.0002   0.5420  0.0002  0.0003  0.0004  0.0004   0.0007   ±0.24%  2033638  [1.11x] ⇑
     oneof({ weight, arbitrary }, ...)                  3,663,028.90  0.0002   0.6333  0.0003  0.0003  0.0004  0.0004   0.0010   ±0.27%  1831515  (baseline)
   · option(integer())                                  4,115,314.25  0.0001   0.5767  0.0002  0.0003  0.0003  0.0004   0.0009   ±0.41%  2057658  [1.12x] ⇑
     option(integer())                                  3,660,727.58  0.0002   0.5137  0.0003  0.0003  0.0004  0.0004   0.0011   ±0.34%  1830364  (baseline)
   · subarray([1, 2, 3, 4, 5])                          1,931,135.56  0.0002   0.4932  0.0005  0.0007  0.0009  0.0011   0.0016   ±0.39%   965568  [0.98x] ⇓
     subarray([1, 2, 3, 4, 5])                          1,973,071.18  0.0002   0.5455  0.0005  0.0007  0.0010  0.0012   0.0018   ±0.44%   986536  (baseline)
   · letrec(tree)                                         562,368.10  0.0002   0.4573  0.0018  0.0026  0.0080  0.0092   0.0162   ±0.57%   281186  [1.05x] ⇑
     letrec(tree)                                         535,587.89  0.0002   0.4938  0.0019  0.0027  0.0081  0.0093   0.0158   ±0.64%   267794  (baseline)
   · memo(tree)                                           555,783.17  0.0002   0.4331  0.0018  0.0032  0.0066  0.0069   0.0158   ±0.60%   277893  [1.04x] ⇑
     memo(tree)                                           535,038.72  0.0002   0.5078  0.0019  0.0033  0.0068  0.0072   0.0154   ±0.64%   267520  (baseline)
   · anything()                                            84,237.47  0.0003   2.9634  0.0119  0.0197  0.0625  0.0713   0.0953   ±2.28%    42119  [1.03x] ⇑
     anything()                                            81,857.92  0.0003   2.2059  0.0122  0.0203  0.0634  0.0723   0.0989   ±2.22%    40929  (baseline)
   · json()                                                77,239.96  0.0004   4.9007  0.0129  0.0213  0.0664  0.0769   0.1004   ±2.70%    38620  [1.02x] ⇑
     json()                                                75,468.84  0.0004   5.0457  0.0133  0.0219  0.0664  0.0768   0.1033   ±2.80%    37736  (baseline)
   · entityGraph(node -> node)                              6,611.98  0.0094  25.5121  0.1512  0.1345  0.3649  0.4392   9.5252  ±14.90%     3306  [0.93x] ⇓
     entityGraph(node -> node)                              7,117.97  0.0088  10.8812  0.1405  0.1303  0.2399  0.2901  10.4778  ±13.38%     3560  (baseline)
   · emailAddress()                                        98,229.11  0.0023   0.3472  0.0102  0.0131  0.0204  0.0250   0.0309   ±0.53%    49137  [1.14x] ⇑
     emailAddress()                                        85,925.43  0.0024  25.9800  0.0116  0.0138  0.0309  0.0395   0.0524  ±10.23%    42963  (baseline)
   · webUrl()                                             176,192.97  0.0022   0.3433  0.0057  0.0066  0.0116  0.0187   0.0475   ±0.49%    88097  [1.04x] ⇑
     webUrl()                                             169,725.08  0.0023   0.4372  0.0059  0.0069  0.0109  0.0164   0.0234   ±0.56%    84863  (baseline)
   · ipV4()                                             1,258,561.97  0.0006   0.4610  0.0008  0.0008  0.0011  0.0013   0.0022   ±0.38%   629281  [0.98x] ⇓
     ipV4()                                             1,278,224.34  0.0006   0.4997  0.0008  0.0008  0.0011  0.0014   0.0026   ±0.44%   639113  (baseline)
   · ipV6()                                               247,765.49  0.0005   0.4330  0.0040  0.0049  0.0076  0.0105   0.0185   ±0.45%   123883  [1.02x] ⇑
     ipV6()                                               241,773.95  0.0005   0.4466  0.0041  0.0049  0.0072  0.0098   0.0183   ±0.55%   120888  (baseline)
   · uuid()                                               744,252.35  0.0010   0.4369  0.0013  0.0014  0.0018  0.0020   0.0123   ±0.34%   372127  [1.05x] ⇑
     uuid()                                               710,355.59  0.0010   0.4813  0.0014  0.0014  0.0021  0.0024   0.0122   ±0.46%   355178  (baseline)
   · stringMatching(/^[a-zA-Z0-9]+$/)                     536,730.69  0.0007   0.2424  0.0019  0.0023  0.0035  0.0036   0.0136   ±0.31%   268366  [1.07x] ⇑
     stringMatching(/^[a-zA-Z0-9]+$/)                     501,869.47  0.0007   0.5063  0.0020  0.0025  0.0038  0.0041   0.0137   ±0.56%   250935  (baseline)
   · mixedCase(string())                                  559,313.13  0.0005   0.3402  0.0018  0.0023  0.0033  0.0035   0.0137   ±0.39%   279657  [1.05x] ⇑
     mixedCase(string())                                  533,454.54  0.0005   0.5324  0.0019  0.0023  0.0036  0.0040   0.0134   ±0.54%   266728  (baseline)
   · integer().map(.)                                   5,302,589.54  0.0001   0.6377  0.0002  0.0002  0.0003  0.0003   0.0006   ±0.58%  2651295  [1.04x] ⇑
     integer().map(.)                                   5,113,599.52  0.0001   0.3314  0.0002  0.0002  0.0003  0.0003   0.0007   ±0.50%  2556800  (baseline)
   · integer().chain(.)                                 2,883,936.79  0.0002   0.3729  0.0003  0.0004  0.0005  0.0007   0.0012   ±0.53%  1441969  [0.98x] ⇓
     integer().chain(.)                                 2,949,951.16  0.0002   0.5194  0.0003  0.0004  0.0005  0.0006   0.0013   ±0.56%  1474976  (baseline)
   · integer().filter(.)                                3,128,423.81  0.0001  13.7429  0.0003  0.0004  0.0009  0.0010   0.0014   ±5.39%  1564212  [1.03x] ⇑
     integer().filter(.)                                3,050,499.03  0.0001   0.2944  0.0003  0.0004  0.0010  0.0011   0.0015   ±0.21%  1525250  (baseline)

 ✓  fast-check  test/bench/runners.bench.ts > runner 2426ms
     name                                                     hz     min     max    mean     p75     p99    p995    p999     rme  samples
   · assert on sync predicate not returning anything   12,964.15  0.0739  0.3006  0.0771  0.0750  0.1541  0.1681  0.2248  ±0.39%     6483  [1.04x] ⇑
     assert on sync predicate not returning anything   12,476.54  0.0772  0.3477  0.0802  0.0782  0.1549  0.1651  0.2072  ±0.36%     6239  (baseline)
   · assert on sync predicate returning true           12,905.57  0.0737  0.3335  0.0775  0.0747  0.1513  0.1634  0.2333  ±0.44%     6453  [1.04x] ⇑
     assert on sync predicate returning true           12,407.32  0.0770  0.3582  0.0806  0.0781  0.1560  0.1748  0.2348  ±0.44%     6204  (baseline)
   · assert on async predicate not returning anything   8,723.51  0.1090  0.2981  0.1146  0.1109  0.1935  0.1986  0.2195  ±0.40%     4362  [1.04x] ⇑
     assert on async predicate not returning anything   8,378.16  0.1115  8.0690  0.1194  0.1141  0.1944  0.1995  0.2355  ±3.14%     4190  (baseline)
   · check on sync predicate not returning anything    12,972.22  0.0731  0.3504  0.0771  0.0752  0.1483  0.1571  0.2377  ±0.38%     6487  [1.04x] ⇑
     check on sync predicate not returning anything    12,493.71  0.0771  0.3577  0.0800  0.0781  0.1553  0.1654  0.2445  ±0.38%     6247  (baseline)

 BENCH  Summary

   fast-check  constantFrom(...) - test/bench/arbitraries.bench.ts > generate
    1.13x faster than integer()
    1.15x faster than boolean()
    1.51x faster than maxSafeInteger()
    1.52x faster than integer().map(.)
    1.80x faster than float()
    1.96x faster than option(integer())
    1.98x faster than oneof({ weight, arbitrary }, ...)
    1.98x faster than oneof(integer(), integer())
    2.14x faster than tuple(integer())
    2.57x faster than integer().filter(.)
    2.65x faster than date()
    2.79x faster than integer().chain(.)
    4.17x faster than subarray([1, 2, 3, 4, 5])
    4.82x faster than double()
    4.88x faster than array(integer())
    6.16x faster than record({ a: integer() })
    6.40x faster than ipV4()
    6.64x faster than bigInt()
    6.87x faster than uniqueArray(integer())
    7.77x faster than set(integer())
    7.82x faster than string()
    8.06x faster than base64String()
    10.82x faster than uuid()
    11.74x faster than string({ unit: 'grapheme' })
    14.32x faster than letrec(tree)
    14.40x faster than mixedCase(string())
    14.49x faster than memo(tree)
    15.01x faster than stringMatching(/^[a-zA-Z0-9]+$/)
    26.97x faster than array(integer(), { maxLength: 100, size: 'max' })
    32.51x faster than ipV6()
    42.71x faster than string({ maxLength: 100, size: 'max' })
    45.72x faster than webUrl()
    46.34x faster than map(string(), integer())
    72.82x faster than dictionary(string(), integer())
    82.00x faster than emailAddress()
    95.63x faster than anything()
    104.29x faster than json()
    1218.28x faster than entityGraph(node -> node)

   fast-check  check on sync predicate not returning anything - test/bench/runners.bench.ts > runner
    1.00x faster than assert on sync predicate not returning anything
    1.01x faster than assert on sync predicate returning true
    1.49x faster than assert on async predicate not returning anything

Benchmark report written to /home/runner/work/fast-check/fast-check/benchmark.json

Commit: d2429e9 (merge: e23c681)

@dubzzz
dubzzz merged commit db00e2a into main Jun 4, 2026
52 checks passed
@dubzzz
dubzzz deleted the claude/perf-dict-mapper-direct-assign branch June 4, 2026 22:02
@codecov

codecov Bot commented Jun 4, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 94.87%. Comparing base (dc9f32a) to head (d2429e9).
⚠️ Report is 2 commits behind head on main.

Additional details and impacted files
@@           Coverage Diff           @@
##             main    #7047   +/-   ##
=======================================
  Coverage   94.86%   94.87%           
=======================================
  Files         212      212           
  Lines        5884     5889    +5     
  Branches     1543     1545    +2     
=======================================
+ Hits         5582     5587    +5     
  Misses        294      294           
  Partials        8        8           
Flag Coverage Δ
tests 94.87% <100.00%> (+<0.01%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

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.

2 participants