Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore(perf): asStringSmall slower than JSON.stringify #694

Closed
wants to merge 1 commit into from

Conversation

cesco69
Copy link
Contributor

@cesco69 cesco69 commented Mar 12, 2024

asStringSmall seem slower than JSON.stringify

online benchmark

it seems better to use JSON.stringify instead of asStringSmall

In detail:

// Test cases ...
const STR_1  = 'A'
const STR_10 = STR_1.repeat(10) // 10 chars
const STR_20 = STR_1.repeat(20) // 20 chars
const STR_30 = STR_1.repeat(30) // 30 chars
const STR_40 = STR_1.repeat(40) // 40 chars
asStringSmall(STR_1)   221,230 ops/s
JSON.stringify(STR_1)  639,110 ops/s

asStringSmall(STR_10)   73,130 ops/s
JSON.stringify(STR_10) 598,020 ops/s

asStringSmall(STR_20)   38,900 ops/s
JSON.stringify(STR_20) 596,480 ops/s

asStringSmall(STR_30)   29,960 ops/s
JSON.stringify(STR_30) 413,430 ops/s

asStringSmall(STR_40)   21,840 ops/s
JSON.stringify(STR_40) 460,970 ops/s

JSON.stringify win in all cases.

V8 has optimized version of string escaping EscapeJSONStringImpl and EscapeSpecialCodePoint.
A javascript implementation will never beat these in C++

This make JSON.stringify always faster on small string. The idea is just skip JSON.stringify on medium string without char to escape:

asString (str) {
    // SMALL STRING: JSON.stringify is always faster
    if (str.length < 42) {
      return JSON.stringify(str)
    // MEDIUM STRING: the check of, if string has char to escape, is fast on not big string
    } else if (str.length < 5000 && STR_ESCAPE.test(str) === false) {
      // Only use the regular expression for shorter input. The overhead is otherwise too much.
      return '"' + str + '"'
    // BIG STRING
    } else {
      return JSON.stringify(str)
    }
}

Checklist

@cesco69 cesco69 changed the title asStringSmall seem slower than JSON.stringify chore(perf): asStringSmall slower than JSON.stringify Mar 12, 2024
Copy link
Member

@gurgunday gurgunday left a comment

Choose a reason for hiding this comment

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

looks good to me, I'd recommend checking the provided V8 code

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.

While I would believe the provided benchmark, our numbers shows a different story.

This PR:

short string............................................. x 11,870,831 ops/sec ±0.88% (192 runs sampled)
unsafe short string...................................... x 813,671,948 ops/sec ±0.11% (195 runs sampled)
short string with double quote........................... x 11,593,521 ops/sec ±0.83% (192 runs sampled)
long string without double quotes........................ x 16,262 ops/sec ±0.15% (195 runs sampled)
unsafe long string without double quotes................. x 808,251,968 ops/sec ±0.26% (194 runs sampled)
long string.............................................. x 16,004 ops/sec ±0.13% (194 runs sampled)
unsafe long string....................................... x 810,901,758 ops/sec ±0.24% (194 runs sampled)
number................................................... x 813,227,086 ops/sec ±0.16% (189 runs sampled)
integer.................................................. x 208,602,841 ops/sec ±0.13% (195 runs sampled)
formatted date-time...................................... x 1,412,455 ops/sec ±0.70% (192 runs sampled)
formatted date........................................... x 1,063,282 ops/sec ±0.15% (193 runs sampled)
formatted time........................................... x 1,060,232 ops/sec ±0.60% (194 runs sampled)
short array of numbers................................... x 77,987 ops/sec ±0.19% (194 runs sampled)
short array of integers.................................. x 64,438 ops/sec ±0.18% (194 runs sampled)
short array of short strings............................. x 11,024 ops/sec ±0.20% (195 runs sampled)
short array of long strings.............................. x 11,061 ops/sec ±0.16% (194 runs sampled)
short array of objects with properties of different types x 6,862 ops/sec ±0.17% (194 runs sampled)
object with number property.............................. x 809,881,883 ops/sec ±0.10% (193 runs sampled)
object with integer property............................. x 207,814,688 ops/sec ±0.15% (195 runs sampled)
object with short string property........................ x 12,129,248 ops/sec ±0.20% (194 runs sampled)
object with long string property......................... x 15,989 ops/sec ±0.16% (195 runs sampled)
object with properties of different types................ x 1,407,092 ops/sec ±0.19% (194 runs sampled)
simple object............................................ x 5,209,219 ops/sec ±0.27% (195 runs sampled)
simple object with required fields....................... x 5,354,476 ops/sec ±0.16% (194 runs sampled)
object with const string property........................ x 814,753,997 ops/sec ±0.08% (188 runs sampled)
object with const number property........................ x 812,281,777 ops/sec ±0.19% (194 runs sampled)
object with const bool property.......................... x 815,316,574 ops/sec ±0.15% (181 runs sampled)
object with const object property........................ x 814,527,351 ops/sec ±0.09% (192 runs sampled)
object with const null property.......................... x 815,133,730 ops/sec ±0.12% (194 runs sampled)

master

short string............................................. x 21,728,499 ops/sec ±0.87% (193 runs sampled)
unsafe short string...................................... x 815,436,755 ops/sec ±0.11% (184 runs sampled)
short string with double quote........................... x 13,268,536 ops/sec ±0.63% (193 runs sampled)
long string without double quotes........................ x 16,203 ops/sec ±0.32% (195 runs sampled)
unsafe long string without double quotes................. x 816,105,659 ops/sec ±0.09% (188 runs sampled)
long string.............................................. x 16,032 ops/sec ±0.16% (194 runs sampled)
unsafe long string....................................... x 814,770,840 ops/sec ±0.21% (183 runs sampled)
number................................................... x 816,578,188 ops/sec ±0.08% (181 runs sampled)
integer.................................................. x 208,419,109 ops/sec ±0.16% (193 runs sampled)
formatted date-time...................................... x 1,416,343 ops/sec ±0.25% (195 runs sampled)
formatted date........................................... x 1,030,506 ops/sec ±0.88% (192 runs sampled)
formatted time........................................... x 1,039,128 ops/sec ±0.75% (191 runs sampled)
short array of numbers................................... x 78,479 ops/sec ±0.16% (194 runs sampled)
short array of integers.................................. x 63,797 ops/sec ±0.62% (194 runs sampled)
short array of short strings............................. x 19,181 ops/sec ±0.50% (192 runs sampled)
short array of long strings.............................. x 19,258 ops/sec ±0.43% (193 runs sampled)
short array of objects with properties of different types x 9,395 ops/sec ±1.04% (188 runs sampled)
object with number property.............................. x 813,748,830 ops/sec ±0.12% (180 runs sampled)
object with integer property............................. x 208,023,114 ops/sec ±0.15% (194 runs sampled)
object with short string property........................ x 21,680,186 ops/sec ±1.17% (194 runs sampled)
object with long string property......................... x 15,958 ops/sec ±0.29% (193 runs sampled)
object with properties of different types................ x 2,018,270 ops/sec ±0.77% (191 runs sampled)
simple object............................................ x 9,878,794 ops/sec ±0.65% (192 runs sampled)
simple object with required fields....................... x 10,050,716 ops/sec ±0.73% (193 runs sampled)
object with const string property........................ x 817,234,834 ops/sec ±0.10% (180 runs sampled)
object with const number property........................ x 810,666,824 ops/sec ±0.29% (195 runs sampled)
object with const bool property.......................... x 810,548,546 ops/sec ±0.27% (191 runs sampled)
object with const object property........................ x 814,996,978 ops/sec ±0.17% (186 runs sampled)
object with const null property.......................... x 813,462,610 ops/sec ±0.20% (193 runs sampled)

Not only the short string benchmark degrades, but also the one for simple objects.

our benchmark stress the library in its entirety, and while this analysis hold true in isolation, the speed profile change completely when inlining is taken into consideration.

@mcollina
Copy link
Member

V8 has optimized version of string escaping EscapeJSONStringImpl and EscapeSpecialCodePoint.
A javascript implementation will never beat these in C++

The problem is that you need to reach C++ to execute those, and that barrier is not cheap.

@cesco69
Copy link
Contributor Author

cesco69 commented Mar 13, 2024

mhh ok.. thanks!

@cesco69 cesco69 closed this Mar 13, 2024
@cesco69 cesco69 deleted the patch-2 branch March 13, 2024 09:08
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.

3 participants