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

[Merged by Bors] - Improve JsString performance #2042

Closed
wants to merge 14 commits into from
Closed

Conversation

YXL76
Copy link
Contributor

@YXL76 YXL76 commented Apr 23, 2022

It changes the following:

  • The current JsString implementation has a lot of duplicate heap allocations. For static strings, JsString only needs to store the index of CONSTANTS_ARRAY. I let JsString can store pointer to heap or static area. The two are distinguished by the first bit. (This implementation is rough, maybe we should put flag into Inner, like arcstr)
  • I also added more string constants, which are always used.

Signed-off-by: YXL <chenxin.lan.76@gmail.com>
Signed-off-by: YXL <chenxin.lan.76@gmail.com>
@YXL76
Copy link
Contributor Author

YXL76 commented Apr 23, 2022

At present, the Rc feature of JsString is very limited, just like the function, it always performs two heap allocations instead of increasing the reference count. (See here)

Since we always do hashing, wouldn't it be better to use get_or_insert_with instead of get?

@codecov
Copy link

codecov bot commented Apr 23, 2022

Codecov Report

Merging #2042 (698a517) into main (5b46d12) will increase coverage by 0.23%.
The diff coverage is 48.93%.

@@            Coverage Diff             @@
##             main    #2042      +/-   ##
==========================================
+ Coverage   43.90%   44.14%   +0.23%     
==========================================
  Files         212      212              
  Lines       18734    18841     +107     
==========================================
+ Hits         8226     8318      +92     
- Misses      10508    10523      +15     
Impacted Files Coverage Δ
boa_engine/src/bigint.rs 37.59% <0.00%> (ø)
boa_engine/src/string.rs 53.93% <51.11%> (-5.09%) ⬇️
...oa_engine/src/syntax/parser/statement/block/mod.rs 46.34% <0.00%> (-6.30%) ⬇️
...ngine/src/syntax/parser/statement/try_stm/catch.rs 45.67% <0.00%> (-4.33%) ⬇️
...src/syntax/parser/statement/declaration/lexical.rs 30.59% <0.00%> (-3.60%) ⬇️
...syntax/parser/statement/iteration/for_statement.rs 38.82% <0.00%> (-3.52%) ⬇️
.../statement/declaration/hoistable/class_decl/mod.rs 24.13% <0.00%> (-0.83%) ⬇️
...a_engine/src/syntax/parser/statement/switch/mod.rs 39.53% <0.00%> (-0.47%) ⬇️
boa_engine/src/object/mod.rs 20.68% <0.00%> (-0.41%) ⬇️
... and 38 more

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 5b46d12...698a517. Read the comment docs.

Signed-off-by: YXL <chenxin.lan.76@gmail.com>
@YXL76
Copy link
Contributor Author

YXL76 commented Apr 23, 2022

Simplify it, now inner only stores the index of CONSTANTS_ARRAY

Signed-off-by: YXL <chenxin.lan.76@gmail.com>
Copy link
Member

@raskad raskad left a comment

Choose a reason for hiding this comment

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

The principle used here is a tagged pointer right? There should be an explicit mention of that in the comments, so that everyone knows what is being done.

I'm not too familiar with the platform requirements for tagged pointers. I assume this would fail on 16 bit platforms? Are there any other requirements that should be noted?
I think if we implement this, we would have to at least mention these requirements, at best prevent boa from compiling if any of the requirements are not met.

boa_engine/src/string.rs Outdated Show resolved Hide resolved
boa_engine/src/value/mod.rs Outdated Show resolved Hide resolved
boa_engine/src/bigint.rs Show resolved Hide resolved
boa_engine/src/bigint.rs Show resolved Hide resolved
@raskad raskad added the enhancement New feature or request label Apr 23, 2022
Copy link
Member

@jedel1043 jedel1043 left a comment

Choose a reason for hiding this comment

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

I like the idea of this PR very much! However, I think it would be good to check with miri if the pointer provenance of our Inner pointer is preserved between casts.

@YXL76
Copy link
Contributor Author

YXL76 commented Apr 24, 2022

The principle used here is a tagged pointer right? There should be an explicit mention of that in the comments, so that everyone knows what is being done.

I'm not too familiar with the platform requirements for tagged pointers. I assume this would fail on 16 bit platforms? Are there any other requirements that should be noted? I think if we implement this, we would have to at least mention these requirements, at best prevent boa from compiling if any of the requirements are not met.

It works on 16 bit machine, because we only use the first bit. So far there is no side effect of introducing this technique, since rust-gc also uses this technique.

https://github.com/Manishearth/rust-gc/blob/0278c6b895d53153a4eb63cfc56e59d97698e5ed/gc/src/lib.rs#L102-L107

https://github.com/Manishearth/rust-gc/blob/0278c6b895d53153a4eb63cfc56e59d97698e5ed/gc/src/lib.rs#L114-L118

Signed-off-by: YXL <chenxin.lan.76@gmail.com>
Signed-off-by: YXL <chenxin.lan.76@gmail.com>
Signed-off-by: YXL <chenxin.lan.76@gmail.com>
Signed-off-by: YXL <chenxin.lan.76@gmail.com>
boa_engine/src/string.rs Outdated Show resolved Hide resolved
Signed-off-by: YXL <chenxin.lan.76@gmail.com>
@YXL76
Copy link
Contributor Author

YXL76 commented Apr 25, 2022

A larger MAX_CONSTANT_STRING_LENGTH may have some side effects, we have to do more hash table queries, and the hit rate is low.

This goes back to my previous question, should CONSTANTS be a fixed size HashSet? If so, then we should strictly limit the size of MAX_CONSTANT_STRING_LENGTH, if not (ie as a global cache), then there are no such problems.

@raskad
Copy link
Member

raskad commented Apr 25, 2022

A larger MAX_CONSTANT_STRING_LENGTH may have some side effects, we have to do more hash table queries, and the hit rate is low.

I think this is a performance property that we would have to adjust based on real-word benchmarks. In theory a longer MAX_CONSTANT_STRING_LENGTH leads to more lookups at JsString creation time, but how does that impact the performance of the runtime overall? Do real-world programs benefit more from static strings or from less hashmap lookups?

Maybe there is some research already done in other engines that we could base a decision upon, but I think for now this is totally fine.

This goes back to my previous question, should CONSTANTS be a fixed size HashSet? If so, then we should strictly limit the size of MAX_CONSTANT_STRING_LENGTH, if not (ie as a global cache), then there are no such problems.

I think inserting into CONSTANTS is not realistic. We either leak memory by never deleting those strings or would have to implement reference counting in CONSTANTS to make removing from CONSTANTS possible which seems like a bad idea for dealloc performance.

@jedel1043
Copy link
Member

jedel1043 commented Apr 26, 2022

As expected, running MIRIFLAGS="-Zmiri-strict-provenance -Zmiri-disable-stacked-borrows" cargo +nightly miri test --package boa_engine --lib -- string::tests --skip builtins fails with the message:

error: Undefined Behavior: dereferencing pointer failed: 0xdb4690 is not a valid pointer

Meaning the provenance of the pointer is not preserved. The fix is pretty easy, we just need to use NonNull<Inner> instead of NonZeroUsize, casting the tagged indexes to invalid pointers with index as *const Inner. That should preserve the provenance of every valid pointer.

Signed-off-by: YXL <chenxin.lan.76@gmail.com>
@Razican
Copy link
Member

Razican commented Apr 26, 2022

Benchmarks

Click to view benchmark
name baseDuration changesDuration difference
Arithmetic operations (Compiler) 493.7±1.24ns 526.0±0.90ns +7.0 %
Arithmetic operations (Execution) 445.5±1.31ns 487.7±1.37ns +9.0 %
Arithmetic operations (Parser) 6.1±0.02µs 6.2±0.04µs +1.0 %
Array access (Compiler) 1338.8±5.43ns 1374.3±6.68ns +3.0 %
Array access (Execution) 8.5±0.02µs 8.5±0.08µs 0.0 %
Array access (Parser) 13.9±0.04µs 14.1±0.06µs +2.0 %
Array creation (Compiler) 1961.4±19.27ns 1945.9±6.06ns -0.99 %
Array creation (Execution) 2.4±0.00ms 2.5±0.00ms +3.0 %
Array creation (Parser) 15.7±0.04µs 15.8±0.22µs 0.0 %
Array pop (Compiler) 3.7±0.01µs 3.8±0.01µs +4.0 %
Array pop (Execution) 1075.4±13.56µs 1107.8±11.13µs +3.0 %
Array pop (Parser) 157.2±0.25µs 157.2±0.18µs 0.0 %
Boolean Object Access (Compiler) 1110.1±5.17ns 1126.9±3.66ns +2.0 %
Boolean Object Access (Execution) 4.6±0.02µs 4.5±0.01µs -2.0 %
Boolean Object Access (Parser) 16.2±0.03µs 16.7±0.06µs +3.0 %
Clean js (Compiler) 3.7±0.01µs 3.7±0.01µs 0.0 %
Clean js (Execution) 703.3±6.39µs 681.9±3.80µs -2.9 %
Clean js (Parser) 33.8±0.21µs 34.1±0.05µs +1.0 %
Create Realm 265.1±0.37ns 273.5±1.32ns +3.0 %
Dynamic Object Property Access (Compiler) 1752.9±6.21ns 1769.6±4.42ns +1.0 %
Dynamic Object Property Access (Execution) 5.7±0.04µs 5.6±0.01µs -0.99 %
Dynamic Object Property Access (Parser) 12.1±0.06µs 12.3±0.08µs +2.0 %
Fibonacci (Compiler) 2.4±0.00µs 2.4±0.00µs 0.0 %
Fibonacci (Execution) 1400.4±2.33µs 1445.6±14.19µs +3.0 %
Fibonacci (Parser) 18.5±0.07µs 19.0±0.03µs +3.0 %
For loop (Compiler) 2.1±0.00µs 2.2±0.03µs +5.0 %
For loop (Execution) 15.2±0.06µs 15.4±0.07µs +1.0 %
For loop (Parser) 16.0±0.05µs 16.1±0.05µs 0.0 %
Mini js (Compiler) 3.6±0.01µs 3.5±0.01µs -2.0 %
Mini js (Execution) 657.5±4.75µs 649.1±4.81µs -0.99 %
Mini js (Parser) 30.0±0.12µs 30.4±0.05µs +1.0 %
Number Object Access (Compiler) 1029.0±2.34ns 1060.2±4.30ns +3.0 %
Number Object Access (Execution) 3.5±0.02µs 3.6±0.01µs +2.0 %
Number Object Access (Parser) 12.8±0.11µs 13.0±0.04µs +2.0 %
Object Creation (Compiler) 1434.7±3.62ns 1492.4±4.71ns +4.0 %
Object Creation (Execution) 5.2±0.02µs 5.2±0.07µs -0.99 %
Object Creation (Parser) 10.6±0.06µs 10.8±0.05µs +2.0 %
RegExp (Compiler) 1725.3±3.85ns 1691.2±29.10ns -2.0 %
RegExp (Execution) 11.7±0.06µs 11.9±0.06µs +1.0 %
RegExp (Parser) 11.8±0.25µs 11.9±0.06µs +1.0 %
RegExp Creation (Compiler) 1531.4±3.00ns 1502.1±5.63ns -2.0 %
RegExp Creation (Execution) 8.8±0.02µs 8.8±0.04µs 0.0 %
RegExp Creation (Parser) 9.7±0.11µs 9.8±0.03µs +2.0 %
RegExp Literal (Compiler) 1754.2±38.01ns 1684.2±15.68ns -3.8 %
RegExp Literal (Execution) 11.6±0.05µs 11.9±0.06µs +2.0 %
RegExp Literal (Parser) 9.5±0.04µs 9.7±0.02µs +2.0 %
RegExp Literal Creation (Compiler) 1510.7±10.96ns 1512.1±5.35ns 0.0 %
RegExp Literal Creation (Execution) 8.9±0.03µs 8.8±0.03µs -0.99 %
RegExp Literal Creation (Parser) 7.5±0.05µs 7.7±0.02µs +2.0 %
Static Object Property Access (Compiler) 1486.9±4.70ns 1470.8±4.04ns -0.99 %
Static Object Property Access (Execution) 5.4±0.06µs 5.3±0.02µs -0.99 %
Static Object Property Access (Parser) 11.4±0.06µs 11.5±0.06µs +1.0 %
String Object Access (Compiler) 1550.9±23.24ns 1546.9±6.29ns 0.0 %
String Object Access (Execution) 6.1±0.02µs 6.7±0.13µs +9.0 %
String Object Access (Parser) 15.9±0.02µs 16.5±0.02µs +4.0 %
String comparison (Compiler) 2.2±0.01µs 2.2±0.01µs -3.8 %
String comparison (Execution) 4.7±0.03µs 4.8±0.06µs +2.0 %
String comparison (Parser) 12.5±0.04µs 12.8±0.04µs +3.0 %
String concatenation (Compiler) 1735.4±4.99ns 1673.5±3.89ns -3.8 %
String concatenation (Execution) 4.6±0.03µs 4.6±0.04µs 0.0 %
String concatenation (Parser) 8.7±0.03µs 8.9±0.01µs +2.0 %
String copy (Compiler) 1366.5±5.89ns 1339.6±5.52ns -2.0 %
String copy (Execution) 4.3±0.02µs 4.3±0.04µs +2.0 %
String copy (Parser) 6.5±0.03µs 6.7±0.03µs +3.0 %
Symbols (Compiler) 941.6±3.70ns 983.6±3.43ns +4.0 %
Symbols (Execution) 4.4±0.01µs 4.3±0.03µs -2.0 %
Symbols (Parser) 5.0±0.02µs 5.2±0.02µs +4.0 %

Copy link
Member

@jedel1043 jedel1043 left a comment

Choose a reason for hiding this comment

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

Some more comments about the implementation. I also tinkered a bit with the documentation to make it a bit more clearer.

boa_engine/src/string.rs Outdated Show resolved Hide resolved
boa_engine/src/string.rs Outdated Show resolved Hide resolved
boa_engine/src/string.rs Outdated Show resolved Hide resolved
boa_engine/src/string.rs Outdated Show resolved Hide resolved
boa_engine/src/string.rs Outdated Show resolved Hide resolved
boa_engine/src/string.rs Outdated Show resolved Hide resolved
boa_engine/src/string.rs Outdated Show resolved Hide resolved
boa_engine/src/string.rs Outdated Show resolved Hide resolved
boa_engine/src/string.rs Outdated Show resolved Hide resolved
boa_engine/src/string.rs Outdated Show resolved Hide resolved
Signed-off-by: YXL <chenxin.lan.76@gmail.com>
Signed-off-by: YXL <chenxin.lan.76@gmail.com>
Copy link
Member

@jedel1043 jedel1043 left a comment

Choose a reason for hiding this comment

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

Nice work! There are still some nitpicks I have, but it's looking pretty good nonetheless :)

boa_engine/src/string.rs Outdated Show resolved Hide resolved
boa_engine/src/string.rs Outdated Show resolved Hide resolved
Signed-off-by: YXL <chenxin.lan.76@gmail.com>
Copy link
Member

@jedel1043 jedel1043 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! Let's wait until the ones who left comments approve 😄

boa_engine/src/string.rs Show resolved Hide resolved
Signed-off-by: YXL <chenxin.lan.76@gmail.com>
Copy link
Member

@HalidOdat HalidOdat 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! :)

@raskad
Copy link
Member

raskad commented May 1, 2022

bors r+

bors bot pushed a commit that referenced this pull request May 1, 2022
It changes the following:

- The current `JsString` implementation has a lot of duplicate heap allocations. For static strings, `JsString` only needs to store the index of `CONSTANTS_ARRAY`. ~~I let `JsString` can store pointer to heap or static area. The two are distinguished by the first bit. (This implementation is rough, maybe we should put flag into `Inner`, like [arcstr](https://github.com/thomcc/arcstr/blob/70ba2fac19d3efe8d2e0231daaf74f9987c04b8a/src/arc_str.rs#L751-L757))~~
- I also added more string constants, which are always used.
@bors
Copy link

bors bot commented May 1, 2022

Pull request successfully merged into main.

Build succeeded:

@bors bors bot changed the title Improve JsString performance [Merged by Bors] - Improve JsString performance May 1, 2022
@bors bors bot closed this May 1, 2022
@YXL76 YXL76 deleted the string branch May 2, 2022 05:38
@raskad raskad added this to the v0.15.0 milestone May 13, 2022
Razican pushed a commit that referenced this pull request Jun 8, 2022
It changes the following:

- The current `JsString` implementation has a lot of duplicate heap allocations. For static strings, `JsString` only needs to store the index of `CONSTANTS_ARRAY`. ~~I let `JsString` can store pointer to heap or static area. The two are distinguished by the first bit. (This implementation is rough, maybe we should put flag into `Inner`, like [arcstr](https://github.com/thomcc/arcstr/blob/70ba2fac19d3efe8d2e0231daaf74f9987c04b8a/src/arc_str.rs#L751-L757))~~
- I also added more string constants, which are always used.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants