Introduce concurrency to asynchronous file linting for improved performance.

This commit is contained in:
David Anson 2020-09-12 12:01:20 -07:00
parent 2851a691ba
commit 4286f68152
3 changed files with 101 additions and 54 deletions

View file

@ -90,7 +90,9 @@ function newResults(ruleList) {
function toString(useAlias) {
let ruleNameToRule = null;
const results = [];
Object.keys(lintResults).forEach(function forFile(file) {
const keys = Object.keys(lintResults);
keys.sort();
keys.forEach(function forFile(file) {
const fileResults = lintResults[file];
if (Array.isArray(fileResults)) {
fileResults.forEach(function forResult(result) {
@ -727,53 +729,98 @@ function lintInput(options, synchronous, callback) {
md.use(...plugin);
});
const results = newResults(ruleList);
// Helper to lint the next string or file
/* eslint-disable consistent-return */
let done = false;
// Linting of strings is always synchronous
let syncItem = null;
// eslint-disable-next-line jsdoc/require-jsdoc
function lintNextItem() {
let iterating = true;
let item = null;
// eslint-disable-next-line jsdoc/require-jsdoc
function lintNextItemCallback(err, result) {
if (err) {
iterating = false;
return callback(err);
}
results[item] = result;
return iterating || lintNextItem();
}
while (iterating) {
if ((item = stringsKeys.shift())) {
lintContent(
ruleList,
item,
strings[item] || "",
md,
config,
frontMatter,
handleRuleFailures,
noInlineConfig,
resultVersion,
lintNextItemCallback);
} else if ((item = files.shift())) {
iterating = synchronous;
lintFile(
ruleList,
item,
md,
config,
frontMatter,
handleRuleFailures,
noInlineConfig,
resultVersion,
synchronous,
lintNextItemCallback);
} else {
return callback(null, results);
}
function syncCallback(err, result) {
if (err) {
done = true;
return callback(err);
}
results[syncItem] = result;
return null;
}
return lintNextItem();
while (!done && (syncItem = stringsKeys.shift())) {
lintContent(
ruleList,
syncItem,
strings[syncItem] || "",
md,
config,
frontMatter,
handleRuleFailures,
noInlineConfig,
resultVersion,
syncCallback
);
}
if (synchronous) {
// Lint files synchronously
while (!done && (syncItem = files.shift())) {
lintFile(
ruleList,
syncItem,
md,
config,
frontMatter,
handleRuleFailures,
noInlineConfig,
resultVersion,
synchronous,
syncCallback
);
}
return done || callback(null, results);
}
// Lint files asynchronously
let concurrency = 0;
// eslint-disable-next-line jsdoc/require-jsdoc
function lintConcurrently() {
const asyncItem = files.shift();
if (done) {
// Nothing to do
} else if (asyncItem) {
concurrency++;
lintFile(
ruleList,
asyncItem,
md,
config,
frontMatter,
handleRuleFailures,
noInlineConfig,
resultVersion,
synchronous,
(err, result) => {
concurrency--;
if (err) {
done = true;
return callback(err);
}
results[asyncItem] = result;
lintConcurrently();
return null;
}
);
} else if (concurrency === 0) {
done = true;
return callback(null, results);
}
return null;
}
// Testing on a Raspberry Pi 4 Model B with an artificial 5ms file access
// delay suggests that a concurrency factor of 8 can eliminate the impact
// of that delay (i.e., total time is the same as with no delay).
lintConcurrently();
lintConcurrently();
lintConcurrently();
lintConcurrently();
lintConcurrently();
lintConcurrently();
lintConcurrently();
lintConcurrently();
return null;
}
/**