-
-
Notifications
You must be signed in to change notification settings - Fork 2.8k
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
[Refactor] improve performance of rule merging #3281
Conversation
Codecov Report
@@ Coverage Diff @@
## master #3281 +/- ##
==========================================
- Coverage 97.72% 97.72% -0.01%
==========================================
Files 123 123
Lines 8745 8744 -1
Branches 3170 3165 -5
==========================================
- Hits 8546 8545 -1
Misses 199 199
Continue to review full report at Codecov.
|
allKeys.forEach((instruction) => { | ||
updatedRuleInstructions[instruction] = (node) => { | ||
if (instruction in detectionInstructions) { | ||
detectionInstructions[instruction](node); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This property access performed for each AST node turned out to be performance bottleneck.
lib/util/Components.js
Outdated
for (const key of handlersByKey.keys()) { | ||
const fns = handlersByKey.get(key); | ||
rule[key] = function mergedHandler(node) { | ||
for (const fn of fns) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
for-of is slow; fns.forEach((node) => fn(node))
is almost certainly faster (and cleaner; loops are bad)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
for-of is not slow in node, v8 have done optimizations to make it performs comparable to for loop.
const N = 100000;
const length = 100;
const array = new Array(length).fill().map((_, i) => i);
function benchmarkForLoop() {
let start = performance.now();
let x = 0;
for (let i = 0; i < N; i++) {
for (let j = 0; j < array.length; j++) {
x = (x + array[j]) % 7;
}
}
let end = performance.now();
console.log(end - start);
}
function benchmarkForOfLoop() {
let start = performance.now();
let x = 0;
for (let i = 0; i < N; i++) {
for (const y of array) {
x = (x + y) % 7;
}
}
let end = performance.now();
console.log(end - start);
}
function benchmarkForEach() {
let start = performance.now();
let x = 0;
for (let i = 0; i < N; i++) {
array.forEach((y) => {
x = (x + y) % 7;
});
}
let end = performance.now();
console.log(end - start);
}
benchmarkForLoop();
benchmarkForOfLoop();
benchmarkForEach();
// Output:
// 42.265081003308296
// 47.21948900818825
// 57.372923001646996
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Try those benchmarks without using Array(), which creates a sparse array that .fill may not be able to un-taint.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done!
lib/util/Components.js
Outdated
|
||
/** @type {{[key: string]: Function}} */ | ||
const rule = {}; | ||
for (const key of handlersByKey.keys()) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
same here; let's use handlersByKey.forEach
here
lib/util/Components.js
Outdated
for (const rule of rules) { | ||
for (const key of Object.keys(rule)) { | ||
const fns = handlersByKey.get(key); | ||
if (!fns) { | ||
handlersByKey.set(key, [rule[key]]); | ||
} else { | ||
fns.push(rule[key]); | ||
} | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
for (const rule of rules) { | |
for (const key of Object.keys(rule)) { | |
const fns = handlersByKey.get(key); | |
if (!fns) { | |
handlersByKey.set(key, [rule[key]]); | |
} else { | |
fns.push(rule[key]); | |
} | |
} | |
} | |
rules.forEach((rule) => { | |
Object.keys(rule).forEach((key) => { | |
const fns = handlersByKey.get(key); | |
if (!fns) { | |
handlersByKey.set(key, [rule[key]]); | |
} else { | |
fns.push(rule[key]); | |
} | |
}); | |
}); |
@@ -247,6 +247,39 @@ function getWrapperFunctions(context, pragma) { | |||
]); | |||
} | |||
|
|||
// eslint-disable-next-line valid-jsdoc |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
is there no way to have this rule pass with this function's signature?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That rule has been deprecated for a long time and it lacks support for lots of typescript syntaxes.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
gotcha; so we'd have to use a similar rule from the typescript plugin?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You are looking for eslint-plugin-jsdoc .
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
typescript plugin does not handle jsdoc as far as I know
The performance for merged rule in
Component.detect
is improved. In an end-to-end linting benchmark this change is 4% faster.Benchmark (mean of 5 runs):