Skip to content

Conversation

cesco69
Copy link
Contributor

@cesco69 cesco69 commented Oct 8, 2025

Monomorphic: If a property always gets the same class shape.

It'a a good performance improvments on Array and Object, but not on primitive types

image

Raw benchmark

Checking out "remotes/origin/monomorfic-variables"
Execute "npm run bench"

> fast-json-stringify@6.1.1 bench
> node ./benchmark/bench.js

short string............................................. x 22,911,344 ops/sec ±4.53% (162 runs sampled)
unsafe short string...................................... x 73,650,931 ops/sec ±5.05% (144 runs sampled)
short string with double quote........................... x 9,470,207 ops/sec ±2.16% (150 runs sampled)
long string without double quotes........................ x 36,234 ops/sec ±1.78% (160 runs sampled)
unsafe long string without double quotes................. x 75,229,690 ops/sec ±4.33% (150 runs sampled)
long string.............................................. x 32,157 ops/sec ±2.33% (169 runs sampled)
unsafe long string....................................... x 66,281,516 ops/sec ±4.12% (156 runs sampled)
number................................................... x 75,618,262 ops/sec ±5.33% (140 runs sampled)
integer.................................................. x 57,630,179 ops/sec ±3.43% (140 runs sampled)
formatted date-time...................................... x 690,716 ops/sec ±2.03% (154 runs sampled)
formatted date........................................... x 630,836 ops/sec ±2.90% (152 runs sampled)
formatted time........................................... x 600,154 ops/sec ±2.42% (153 runs sampled)
short array of numbers................................... x 40,187 ops/sec ±1.25% (159 runs sampled)
short array of integers.................................. x 42,814 ops/sec ±2.64% (164 runs sampled)
short array of short strings............................. x 14,476 ops/sec ±4.01% (174 runs sampled)
short array of long strings.............................. x 13,235 ops/sec ±2.99% (161 runs sampled)
short array of objects with properties of different types x 5,675 ops/sec ±2.49% (158 runs sampled)
object with number property.............................. x 91,517,822 ops/sec ±6.67% (152 runs sampled)
object with integer property............................. x 84,343,207 ops/sec ±5.90% (178 runs sampled)
object with short string property........................ x 25,917,292 ops/sec ±1.44% (189 runs sampled)
object with long string property......................... x 47,750 ops/sec ±0.87% (192 runs sampled)
object with properties of different types................ x 1,638,590 ops/sec ±2.34% (183 runs sampled)
simple object............................................ x 9,364,675 ops/sec ±1.50% (185 runs sampled)
simple object with required fields....................... x 9,364,717 ops/sec ±1.52% (184 runs sampled)
object with const string property........................ x 119,169,683 ops/sec ±4.21% (171 runs sampled)
object with const number property........................ x 118,606,339 ops/sec ±4.47% (170 runs sampled)
object with const bool property.......................... x 111,100,119 ops/sec ±4.81% (166 runs sampled)
object with const object property........................ x 118,926,898 ops/sec ±4.21% (172 runs sampled)
object with const null property.......................... x 107,150,234 ops/sec ±5.15% (168 runs sampled)

Checking out "master"
Execute "npm run bench"

> fast-json-stringify@6.1.1 bench
> node ./benchmark/bench.js

short string............................................. x 24,388,745 ops/sec ±2.12% (183 runs sampled)
unsafe short string...................................... x 84,244,363 ops/sec ±6.69% (148 runs sampled)
short string with double quote........................... x 9,644,133 ops/sec ±3.14% (155 runs sampled)
long string without double quotes........................ x 41,509 ops/sec ±4.43% (170 runs sampled)
unsafe long string without double quotes................. x 105,877,016 ops/sec ±5.25% (162 runs sampled)
long string.............................................. x 48,014 ops/sec ±1.04% (194 runs sampled)
unsafe long string....................................... x 117,193,911 ops/sec ±5.03% (169 runs sampled)
number................................................... x 106,082,837 ops/sec ±5.85% (159 runs sampled)
integer.................................................. x 100,649,516 ops/sec ±4.47% (172 runs sampled)
formatted date-time...................................... x 1,249,090 ops/sec ±1.22% (191 runs sampled)
formatted date........................................... x 669,947 ops/sec ±4.04% (152 runs sampled)
formatted time........................................... x 587,616 ops/sec ±0.73% (188 runs sampled)
short array of numbers................................... x 38,787 ops/sec ±1.61% (155 runs sampled)
short array of integers.................................. x 39,458 ops/sec ±2.21% (154 runs sampled)
short array of short strings............................. x 12,415 ops/sec ±2.22% (156 runs sampled)
short array of long strings.............................. x 12,322 ops/sec ±1.40% (155 runs sampled)
short array of objects with properties of different types x 5,810 ops/sec ±1.93% (154 runs sampled)
object with number property.............................. x 66,543,631 ops/sec ±5.88% (145 runs sampled)
object with integer property............................. x 58,760,516 ops/sec ±4.69% (142 runs sampled)
object with short string property........................ x 14,679,314 ops/sec ±2.50% (148 runs sampled)
object with long string property......................... x 39,914 ops/sec ±3.19% (183 runs sampled)
object with properties of different types................ x 1,576,563 ops/sec ±2.94% (162 runs sampled)
simple object............................................ x 8,133,777 ops/sec ±2.20% (180 runs sampled)
simple object with required fields....................... x 8,273,155 ops/sec ±2.32% (176 runs sampled)
object with const string property........................ x 109,001,760 ops/sec ±5.33% (164 runs sampled)
object with const number property........................ x 108,746,170 ops/sec ±5.35% (166 runs sampled)
object with const bool property.......................... x 103,566,758 ops/sec ±6.00% (159 runs sampled)
object with const object property........................ x 112,981,394 ops/sec ±4.63% (167 runs sampled)
object with const null property.......................... x 115,251,679 ops/sec ±4.77% (168 runs sampled)

short string..............................................-6.06%
unsafe short string......................................-12.57%
short string with double quote.............................-1.8%
long string without double quotes........................-12.71%
unsafe long string without double quotes.................-28.95%
long string..............................................-33.03%
unsafe long string.......................................-43.44%
number...................................................-28.72%
integer..................................................-42.74%
formatted date-time.......................................-44.7%
formatted date............................................-5.84%
formatted time............................................+2.13%
short array of numbers....................................+3.61%
short array of integers...................................+8.51%
short array of short strings..............................+16.6%
short array of long strings...............................+7.41%
short array of objects with properties of different types.-2.32%
object with number property..............................+37.53%
object with integer property.............................+43.54%
object with short string property........................+76.56%
object with long string property.........................+19.63%
object with properties of different types.................+3.93%
simple object............................................+15.13%
simple object with required fields.......................+13.19%
object with const string property.........................+9.33%
object with const number property.........................+9.07%
object with const bool property...........................+7.27%
object with const object property.........................+5.26%
object with const null property...........................-7.03%
Back to master 2b37f1d

Signed-off-by: francesco <francesco.bagnoli.69@gmail.com>
Signed-off-by: francesco <francesco.bagnoli.69@gmail.com>
@cesco69 cesco69 marked this pull request as ready for review October 8, 2025 09:26
Signed-off-by: francesco <francesco.bagnoli.69@gmail.com>
@cesco69 cesco69 requested a review from Uzlopak October 8, 2025 15:49
Copy link
Member

@mcollina mcollina left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lgtm

@Uzlopak
Copy link
Contributor

Uzlopak commented Oct 8, 2025

I have no opinion on this, but it feels strange, that primitives drop by nearly 50 %.

@cesco69 cesco69 marked this pull request as draft October 9, 2025 07:08
@cesco69
Copy link
Contributor Author

cesco69 commented Oct 9, 2025

I have no opinion on this, but it feels strange, that primitives drop by nearly 50 %.

Yep, this PR is not good. I want try to fix primitive type

@cesco69
Copy link
Contributor Author

cesco69 commented Oct 9, 2025

I’ve a bad news.

TLTR: the benchmark is not reliable

I was trying to understand why primitive types performed worse than objects in my PR, but I realized that on primitive types the code of the stringify method is the same (PR and Master),

eg, see this benchmark case https://github.com/fastify/fast-json-stringify/blob/main/benchmark/bench.js#L44

This schema

{
  "type": "string"
}

generate in both case (master and PR), this code:

module.exports = function anonymous(validator, serializer
) {

  const {
    asString,
    asNumber,
    asBoolean,
    asDateTime,
    asDate,
    asTime,
    asUnsafeString
  } = serializer

  const asInteger = serializer.asInteger.bind(serializer)


  const JSON_STR_BEGIN_OBJECT = '{'
  const JSON_STR_END_OBJECT = '}'
  const JSON_STR_BEGIN_ARRAY = '['
  const JSON_STR_END_ARRAY = ']'
  const JSON_STR_COMMA = ','
  const JSON_STR_COLONS = ':'
  const JSON_STR_QUOTE = '"'
  const JSON_STR_EMPTY_OBJECT = JSON_STR_BEGIN_OBJECT + JSON_STR_END_OBJECT
  const JSON_STR_EMPTY_ARRAY = JSON_STR_BEGIN_ARRAY + JSON_STR_END_ARRAY
  const JSON_STR_EMPTY_STRING = JSON_STR_QUOTE + JSON_STR_QUOTE
  const JSON_STR_NULL = 'null'

  function main(input) {
    let json = ''

    if (typeof input !== 'string') {
      if (input === null) {
        json += JSON_STR_EMPTY_STRING
      } else if (input instanceof Date) {
        json += JSON_STR_QUOTE + input.toISOString() + JSON_STR_QUOTE
      } else if (input instanceof RegExp) {
        json += asString(input.source)
      } else {
        json += asString(input.toString())
      }
    } else {
      json += asString(input)
    }

    return json
  }

  return main

}(validator, serializer)

The same code had to give the same results... so I decided to benchmark master vs master to see if I got the same results

image

Checking out "master"
Execute "npm run bench"

> fast-json-stringify@6.1.1 bench
> node ./benchmark/bench.js

short string............................................. x 25,906,545 ops/sec ±2.29% (183 runs sampled)
unsafe short string...................................... x 117,503,246 ops/sec ±5.48% (165 runs sampled)
short string with double quote........................... x 16,994,413 ops/sec ±1.85% (187 runs sampled)
long string without double quotes........................ x 58,221 ops/sec ±1.35% (193 runs sampled)
unsafe long string without double quotes................. x 123,399,562 ops/sec ±5.09% (166 runs sampled)
long string.............................................. x 45,787 ops/sec ±2.68% (190 runs sampled)
unsafe long string....................................... x 107,531,043 ops/sec ±5.14% (161 runs sampled)
number................................................... x 115,566,825 ops/sec ±5.00% (163 runs sampled)
integer.................................................. x 99,205,667 ops/sec ±4.17% (170 runs sampled)
formatted date-time...................................... x 1,026,106 ops/sec ±3.43% (179 runs sampled)
formatted date........................................... x 967,191 ops/sec ±3.49% (184 runs sampled)
formatted time........................................... x 1,117,047 ops/sec ±1.31% (193 runs sampled)
short array of numbers................................... x 71,463 ops/sec ±1.94% (186 runs sampled)
short array of integers.................................. x 63,587 ops/sec ±2.36% (183 runs sampled)
short array of short strings............................. x 21,564 ops/sec ±2.08% (188 runs sampled)
short array of long strings.............................. x 20,146 ops/sec ±3.08% (176 runs sampled)
short array of objects with properties of different types x 10,665 ops/sec ±1.43% (186 runs sampled)
object with number property.............................. x 117,962,939 ops/sec ±5.60% (168 runs sampled)
object with integer property............................. x 108,928,099 ops/sec ±3.71% (175 runs sampled)
object with short string property........................ x 27,080,635 ops/sec ±1.51% (188 runs sampled)
object with long string property......................... x 49,784 ops/sec ±1.14% (192 runs sampled)
object with properties of different types................ x 1,719,671 ops/sec ±3.24% (183 runs sampled)
simple object............................................ x 10,356,645 ops/sec ±0.91% (188 runs sampled)
simple object with required fields....................... x 10,100,139 ops/sec ±1.65% (187 runs sampled)
object with const string property........................ x 124,866,455 ops/sec ±4.99% (172 runs sampled)
object with number property.............................. x 117,962,939 ops/sec ±5.60% (168 runs sampled)
object with integer property............................. x 108,928,099 ops/sec ±3.71% (175 runs sampled)
object with short string property........................ x 27,080,635 ops/sec ±1.51% (188 runs sampled)
object with long string property......................... x 49,784 ops/sec ±1.14% (192 runs sampled)
object with properties of different types................ x 1,719,671 ops/sec ±3.24% (183 runs sampled)
simple object............................................ x 10,356,645 ops/sec ±0.91% (188 runs sampled)
simple object with required fields....................... x 10,100,139 ops/sec ±1.65% (187 runs sampled)
object with const string property........................ x 124,866,455 ops/sec ±4.99% (172 runs sampled)
object with const number property........................ x 123,480,168 ops/sec ±4.87% (172 runs sampled)
object with integer property............................. x 108,928,099 ops/sec ±3.71% (175 runs sampled)
object with short string property........................ x 27,080,635 ops/sec ±1.51% (188 runs sampled)
object with long string property......................... x 49,784 ops/sec ±1.14% (192 runs sampled)
object with properties of different types................ x 1,719,671 ops/sec ±3.24% (183 runs sampled)
simple object............................................ x 10,356,645 ops/sec ±0.91% (188 runs sampled)
simple object with required fields....................... x 10,100,139 ops/sec ±1.65% (187 runs sampled)
object with const string property........................ x 124,866,455 ops/sec ±4.99% (172 runs sampled)
object with const number property........................ x 123,480,168 ops/sec ±4.87% (172 runs sampled)
object with long string property......................... x 49,784 ops/sec ±1.14% (192 runs sampled)
object with properties of different types................ x 1,719,671 ops/sec ±3.24% (183 runs sampled)
simple object............................................ x 10,356,645 ops/sec ±0.91% (188 runs sampled)
simple object with required fields....................... x 10,100,139 ops/sec ±1.65% (187 runs sampled)
object with const string property........................ x 124,866,455 ops/sec ±4.99% (172 runs sampled)
object with const number property........................ x 123,480,168 ops/sec ±4.87% (172 runs sampled)
simple object............................................ x 10,356,645 ops/sec ±0.91% (188 runs sampled)
simple object with required fields....................... x 10,100,139 ops/sec ±1.65% (187 runs sampled)
object with const string property........................ x 124,866,455 ops/sec ±4.99% (172 runs sampled)
object with const number property........................ x 123,480,168 ops/sec ±4.87% (172 runs sampled)
object with const string property........................ x 124,866,455 ops/sec ±4.99% (172 runs sampled)
object with const number property........................ x 123,480,168 ops/sec ±4.87% (172 runs sampled)
object with const number property........................ x 123,480,168 ops/sec ±4.87% (172 runs sampled)
object with const bool property.......................... x 110,656,949 ops/sec ±5.57% (170 runs sampled)
object with const object property........................ x 121,682,299 ops/sec ±4.89% (166 runs sampled)
object with const null property.......................... x 115,386,130 ops/sec ±4.61% (167 runs sampled)

Checking out "master"
Execute "npm run bench"

> fast-json-stringify@6.1.1 bench
> node ./benchmark/bench.js

short string............................................. x 25,309,923 ops/sec ±2.39% (180 runs sampled)
unsafe short string...................................... x 95,582,481 ops/sec ±6.63% (157 runs sampled)
short string with double quote........................... x 16,337,301 ops/sec ±2.08% (182 runs sampled)
long string without double quotes........................ x 52,829 ops/sec ±2.25% (182 runs sampled)
unsafe long string without double quotes................. x 113,528,713 ops/sec ±4.57% (166 runs sampled)
long string.............................................. x 46,067 ops/sec ±1.92% (183 runs sampled)
unsafe long string....................................... x 107,277,481 ops/sec ±6.01% (152 runs sampled)
number................................................... x 107,642,408 ops/sec ±5.38% (159 runs sampled)
integer.................................................. x 93,384,162 ops/sec ±4.77% (164 runs sampled)
formatted date-time...................................... x 1,125,076 ops/sec ±2.66% (182 runs sampled)
formatted date........................................... x 901,179 ops/sec ±2.94% (176 runs sampled)
formatted time........................................... x 903,817 ops/sec ±3.39% (182 runs sampled)
short array of numbers................................... x 59,918 ops/sec ±3.20% (175 runs sampled)
short array of integers.................................. x 54,251 ops/sec ±3.20% (168 runs sampled)
short array of short strings............................. x 17,364 ops/sec ±3.48% (173 runs sampled)
short array of long strings.............................. x 17,580 ops/sec ±3.18% (174 runs sampled)
short array of objects with properties of different types x 8,326 ops/sec ±4.31% (173 runs sampled)
object with number property.............................. x 92,969,503 ops/sec ±5.93% (155 runs sampled)
object with integer property............................. x 91,242,327 ops/sec ±4.87% (166 runs sampled)
object with short string property........................ x 22,060,252 ops/sec ±3.22% (170 runs sampled)
object with long string property......................... x 38,606 ops/sec ±3.67% (172 runs sampled)
object with properties of different types................ x 1,671,650 ops/sec ±2.98% (172 runs sampled)
simple object............................................ x 8,164,079 ops/sec ±3.10% (183 runs sampled)
simple object with required fields....................... x 7,888,268 ops/sec ±4.11% (161 runs sampled)
object with const string property........................ x 106,484,075 ops/sec ±5.97% (166 runs sampled)
object with const number property........................ x 114,081,367 ops/sec ±5.23% (162 runs sampled)
object with const bool property.......................... x 105,516,771 ops/sec ±5.75% (156 runs sampled)
object with const object property........................ x 101,331,208 ops/sec ±4.76% (161 runs sampled)
object with const null property.......................... x 103,993,101 ops/sec ±4.73% (159 runs sampled)

short string..............................................+2.36%
unsafe short string......................................+22.93%
short string with double quote............................+4.02%
long string without double quotes........................+10.21%
unsafe long string without double quotes..................+8.69%
long string...............................................-0.61%
unsafe long string........................................+0.24%
number....................................................+7.36%
integer...................................................+6.23%
formatted date-time........................................-8.8%
formatted date............................................+7.33%
formatted time...........................................+23.59%
short array of numbers...................................+19.27%
short array of integers..................................+17.21%
short array of short strings.............................+24.19%
short array of long strings...............................+14.6%
short array of objects with properties of different types+28.09%
object with number property..............................+26.88%
object with integer property.............................+19.38%
object with short string property........................+22.76%
object with long string property.........................+28.95%
object with properties of different types.................+2.87%
simple object............................................+26.86%
simple object with required fields.......................+28.04%
object with const string property........................+17.26%
object with const number property.........................+8.24%
object with const bool property...........................+4.87%
object with const object property........................+20.08%
object with const null property..........................+10.96%
Back to master 2b37f1d

instead, even if I compared the same branch, I got the result that the second run was better than the first one!
I have the impression that the garbage collector give an advantage to the second branch to be executed,

We need a more reliable benchmark to accept PR on performance like this one (see #803).

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.

4 participants