@@ -5,7 +5,7 @@ title: "How ECMAScript Engines Optimize Your Variables"
5
5
authors : boa-dev
6
6
---
7
7
8
- In this post, we will dive into how ECMAScript engines store variables,
8
+ In this post, we will dive into how ECMAScript engines store variables,
9
9
go over storage optimizations, and learn about scope analysis.
10
10
If you are an ECMAScript developer, you will get some practical tips to improve the performance of your code.
11
11
If you write your own ECMAScript engine or any interpreter/compiler, you might get some implementation ideas.
@@ -33,9 +33,10 @@ Let's look at an example to visualize scopes:
33
33
const a = 1 ;
34
34
console .log (a); // 1
35
35
36
- { // <- start of a block scope
37
- const a = 2 ;
38
- console .log (a); // 2
36
+ {
37
+ // <- start of a block scope
38
+ const a = 2 ;
39
+ console .log (a); // 2
39
40
} // <- end of a block scope
40
41
```
41
42
@@ -53,9 +54,9 @@ Let's modify our example to see what happens in that case:
53
54
const a = 1 ;
54
55
55
56
{
56
- const b = 2 ;
57
- console .log (a); // 1
58
- console .log (b); // 2
57
+ const b = 2 ;
58
+ console .log (a); // 1
59
+ console .log (b); // 2
59
60
}
60
61
```
61
62
@@ -71,16 +72,18 @@ Let's look at a more complex example:
71
72
const a = 1 ;
72
73
console .log (a); // 1
73
74
74
- function f () { // <- start of a function scope
75
- var a = 2 ;
76
- console .log (a); // 2
75
+ function f () {
76
+ // <- start of a function scope
77
+ var a = 2 ;
78
+ console .log (a); // 2
77
79
78
- { // <- start of a block scope
79
- let a = 3 ;
80
- console .log (a); // 3
81
- } // <- end of a block scope
80
+ {
81
+ // <- start of a block scope
82
+ let a = 3 ;
83
+ console .log (a); // 3
84
+ } // <- end of a block scope
82
85
83
- console .log (a); // 2
86
+ console .log (a); // 2
84
87
} // <- end of a function scope
85
88
86
89
f ();
@@ -104,9 +107,9 @@ For our proposes we will only work with `let` and `const` and skip those details
104
107
When developing an ECMAScript engine we have to think about how we store and access scopes and variables.
105
108
Take a look at the requirements we have for that storage data structure:
106
109
107
- * A variable maps an identifier to a value.
108
- * A scope can have multiple variables with unique identifiers.
109
- * A scope may have an outer scope.
110
+ - A variable maps an identifier to a value.
111
+ - A scope can have multiple variables with unique identifiers.
112
+ - A scope may have an outer scope.
110
113
111
114
The variables in a scope fit a typical key-value store, like a hashmap.
112
115
The hashmap stores our variable identifiers as keys and the variable values as corresponding values:
@@ -159,8 +162,8 @@ Let's visualize this in an example:
159
162
``` js
160
163
const a = 1 ; // scope index: 0; variable index: 0
161
164
{
162
- const b = 2 ; // scope index: 1; variable index: 0
163
- const c = 3 ; // scope index: 1; variable index: 1
165
+ const b = 2 ; // scope index: 1; variable index: 0
166
+ const c = 3 ; // scope index: 1; variable index: 1
164
167
}
165
168
```
166
169
@@ -172,10 +175,10 @@ Let's explore how unique these indices have to be:
172
175
173
176
``` js
174
177
{
175
- const a = 1 ; // scope index: 1; variable index: 0
178
+ const a = 1 ; // scope index: 1; variable index: 0
176
179
}
177
180
{
178
- const b = 2 ; // scope index: 1; variable index: 0
181
+ const b = 2 ; // scope index: 1; variable index: 0
179
182
}
180
183
```
181
184
@@ -227,8 +230,8 @@ Let's take a look at this example:
227
230
228
231
``` js
229
232
function addOne (a ) {
230
- const one = 1 ;
231
- return one + a;
233
+ const one = 1 ;
234
+ return one + a;
232
235
}
233
236
addOne (2 );
234
237
```
@@ -260,10 +263,10 @@ Let's look at this example:
260
263
261
264
``` js
262
265
function addOneBuilder () {
263
- const one = 1 ;
264
- return (a ) => {
265
- return one + a;
266
- };
266
+ const one = 1 ;
267
+ return (a ) => {
268
+ return one + a;
269
+ };
267
270
}
268
271
const addOne = addOneBuilder ();
269
272
addOne (2 );
@@ -327,10 +330,10 @@ Let's visualize the scope analysis by writing out the scopes for this example:
327
330
328
331
``` js
329
332
function addOneBuilder () {
330
- const one = 1 ;
331
- return (a ) => {
332
- return one + a;
333
- };
333
+ const one = 1 ;
334
+ return (a ) => {
335
+ return one + a;
336
+ };
334
337
}
335
338
```
336
339
@@ -396,69 +399,69 @@ Without going into detail on each of these cases, we can find all of them via sc
396
399
Here is a quick overview:
397
400
398
401
- Non ` strict ` functions create a mapped ` arguments ` object.
399
- The mapped ` arguments ` object can be used to read and write function arguments without using their identifiers.
400
- The reads and writes are kept in sync with the values of the argument variables.
401
- This means that we cannot determine if the argument variables are accessed from outside the function.
402
-
403
- An example of such a situation would be this code:
404
-
405
- ``` js
406
- function f (a ) {
407
- console .log (a); // initial
408
- (() => {
409
- arguments [0 ] = " modified" ;
410
- })()
411
- console .log (a); // modified
412
- }
413
- f (" initial" );
414
- ```
402
+ The mapped ` arguments ` object can be used to read and write function arguments without using their identifiers.
403
+ The reads and writes are kept in sync with the values of the argument variables.
404
+ This means that we cannot determine if the argument variables are accessed from outside the function.
415
405
416
- The solution here is to mark every argument variable that might be accessed through a mapped ` arguments ` object as non - local.
406
+ An example of such a situation would be this code:
417
407
418
- - Direct calls to ` eval` allow potential variable access.
419
- Direct calls to ` eval` have access to the current variables.
420
- Since any code could be executed in ` eval` we cannot do proper scope analysis on any variables in such cases.
408
+ ``` js
409
+ function f (a ) {
410
+ console .log (a); // initial
411
+ (() => {
412
+ arguments [0 ] = " modified" ;
413
+ })();
414
+ console .log (a); // modified
415
+ }
416
+ f (" initial" );
417
+ ```
421
418
422
- An example of direct ` eval ` usage would be this :
419
+ The solution here is to mark every argument variable that might be accessed through a mapped ` arguments ` object as non-local.
423
420
424
- ` ` ` js
425
- function f() {
426
- const a = 1;
427
- eval("function nested() {console.log(a)}; nested();");
428
- }
429
- f();
430
- ` ` `
421
+ - Direct calls to ` eval ` allow potential variable access.
422
+ Direct calls to ` eval ` have access to the current variables.
423
+ Since any code could be executed in ` eval ` we cannot do proper scope analysis on any variables in such cases.
431
424
432
- Our solution is this case is to mark every variable in the scopes where the direct ` eval` call is as non - local.
425
+ An example of direct ` eval ` usage would be this:
433
426
434
- - Usage of the ` with` statement.
435
- Variable identifiers inside a ` with` statement are not static .
436
- A variable identifier could be the access to a variable, but it also could be the access to an object property.
427
+ ``` js
428
+ function f () {
429
+ const a = 1 ;
430
+ eval (" function nested() {console.log(a)}; nested();" );
431
+ }
432
+ f ();
433
+ ```
437
434
438
- See this example :
435
+ Our solution is this case is to mark every variable in the scopes where the direct ` eval ` call is as non-local.
439
436
440
- ` ` ` js
441
- function f() {
442
- const a1 = 1;
443
- for (let i = 0; i < 2; i++) {
444
- with ({ [` a${i}` ]: 2 }) {
445
- console.log(a1);
446
- }
447
- }
437
+ - Usage of the ` with ` statement.
438
+ Variable identifiers inside a ` with ` statement are not static.
439
+ A variable identifier could be the access to a variable, but it also could be the access to an object property.
440
+
441
+ See this example:
442
+
443
+ ``` js
444
+ function f () {
445
+ const a1 = 1 ;
446
+ for (let i = 0 ; i < 2 ; i++ ) {
447
+ with ({ [` a${ i} ` ]: 2 }) {
448
+ console .log (a1);
449
+ }
448
450
}
449
- f();
450
- ` ` `
451
+ }
452
+ f ();
453
+ ```
451
454
452
- In the first loop execution ` a1` is the variable.
453
- In the second loop execution ` a1` is the object property.
454
- As a result of this behavior, every variable accessed inside a ` with` statement cannot be local.
455
+ In the first loop execution ` a1 ` is the variable.
456
+ In the second loop execution ` a1 ` is the object property.
457
+ As a result of this behavior, every variable accessed inside a ` with ` statement cannot be local.
455
458
456
459
## Conclusion
457
460
458
461
After implementing local variables in Boa, we saw significant performance improvements in our benchmarks.
459
462
Our overall benchmark scope improved by more than 25%.
460
463
In one specific benchmark the scope increased by over 70%.
461
- Notice that Boa is not the most performant engine yet.
464
+ Notice that Boa is not the most performant engine yet.
462
465
There are probably other optimizations relating to variable storage that we have not implemented yet.
463
466
464
467
Hopefully, you might have already picked up some practical tips to potentially improve to performance of your ECMAScript code.
0 commit comments