Commit 0ae35c0
[perf]
While looking at a larger code base https://gitlab.com/gitlab-org/gitlab,
`ExportMap.for` seems to take a significant amount of time. More than
half of it is spend on calculating hashes with `hashObject`.
Digging a little deeper, it seems like we are calling it around 500
thousand times. Each iteration calculates the hash of a context object.
This context object is created inside of the `childContext` function and
consists of four parts:
- `settings` -> an Object itself
- `parserOptions` -> an Object itself
- `parserPath` -> a String
- `path` -> a String.
Interestingly `settings`, `parserOptions` and `parserPath` rarely do
change for us, so calculating their hashes on every iteration seems
unnecessary. `hashObject` recursively calculates the hashes of each
key / value pair.
So instead of doing:
```js
cacheKey = hashObject({settings, parserOptions, parserPath, path})
```
We could also do:
```js
cacheKey = parserPath +
hashObject(parserOptions) +
hashObject(settings) +
path
```
This would be just as stable as before, although resulting in longer
cache keys. `parserPath` and `path` would not need to be hashed,
because they are strings and a single character change in them would
result in a different cache key.
Furthermore we can memoize the hashes of `parserOptions` and
`settings`, in case they didn't change compared. The equality is
checked with a simple `JSON.stringify`.
We move this `cacheKey` calculation to `childContext`, adding the
cache key to the `context` object. This way, we can fall back to the
old calculation inside of `ExportMap.for`, as it is a public
interface which consumers might be using.
In our code base the results speak for itself:
- 51.59s spent in `ExportMap.for`, 0ms spent in `childContext`.
- 16.89s is spent in node:crypto/hash `update` (overall)
- 41.02s spent in `ExportMap.for, 1.91s spent in `childContext`.
- Almost no time spent in `hashObject`, actually all calls in
our flame graph come from other code paths
- 7.86s is spent in node:crypto/hash `update` (overall)
So on this machine, and project, we are cutting the execution time
of `ExportMap.for` in half. On machines, which are hashing slower,
the effect might be more pronounced. Similarly machines with less
memory, as the `hashObject` function creates a lot of tiny strings.
One side-effect here could be, that the memoization is in-efficient
if the `settings` or `parserOptions` change often. (I cannot think
of such a scenario, but I am not that versed in the use cases of
this plugin.) But even then, the overhead should mainly be the
`JSON.stringify`.ExportMap: Improve ExportMap.for performance on larger codebases1 parent dc596a2 commit 0ae35c0
2 files changed
+22
-2
lines changed| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
9 | 9 | | |
10 | 10 | | |
11 | 11 | | |
| 12 | + | |
12 | 13 | | |
13 | 14 | | |
14 | 15 | | |
| |||
1066 | 1067 | | |
1067 | 1068 | | |
1068 | 1069 | | |
| 1070 | + | |
1069 | 1071 | | |
1070 | 1072 | | |
1071 | 1073 | | |
| |||
1737 | 1739 | | |
1738 | 1740 | | |
1739 | 1741 | | |
| 1742 | + | |
1740 | 1743 | | |
1741 | 1744 | | |
1742 | 1745 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
305 | 305 | | |
306 | 306 | | |
307 | 307 | | |
308 | | - | |
| 308 | + | |
309 | 309 | | |
310 | 310 | | |
311 | 311 | | |
| |||
559 | 559 | | |
560 | 560 | | |
561 | 561 | | |
562 | | - | |
| 562 | + | |
563 | 563 | | |
564 | 564 | | |
565 | 565 | | |
| |||
781 | 781 | | |
782 | 782 | | |
783 | 783 | | |
| 784 | + | |
| 785 | + | |
| 786 | + | |
| 787 | + | |
784 | 788 | | |
785 | 789 | | |
| 790 | + | |
786 | 791 | | |
787 | 792 | | |
788 | 793 | | |
| 794 | + | |
| 795 | + | |
| 796 | + | |
| 797 | + | |
| 798 | + | |
| 799 | + | |
| 800 | + | |
| 801 | + | |
| 802 | + | |
| 803 | + | |
| 804 | + | |
789 | 805 | | |
| 806 | + | |
790 | 807 | | |
791 | 808 | | |
792 | 809 | | |
| |||
0 commit comments