Skip to content

Commit 92f862b

Browse files
authored
Merge pull request #21115 from calixteman/improve_reftest_perfs2
Improve reftest runner memory usage and load balancing
2 parents 78ba424 + 7810cee commit 92f862b

3 files changed

Lines changed: 177 additions & 111 deletions

File tree

gulpfile.mjs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -168,7 +168,9 @@ function startNode(args, options) {
168168
// (such as `issue6360.pdf`), so we need to restore this value. Note that
169169
// this argument needs to be before all other arguments as it needs to be
170170
// passed to the Node.js process itself and not to the script that it runs.
171-
args.unshift("--max-http-header-size=80000");
171+
// Increase the max heap size to avoid OOM caused by a Puppeteer/BiDi memory
172+
// leak: https://github.com/puppeteer/puppeteer/issues/14876
173+
args.unshift("--max-http-header-size=80000", "--max-old-space-size=8192");
172174
return spawn("node", args, options);
173175
}
174176

test/driver.js

Lines changed: 63 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -549,7 +549,6 @@ class Driver {
549549
};
550550
this._info("User agent: " + navigator.userAgent);
551551
this._log(`Harness thinks this browser is ${this.browser}\n`);
552-
this._log('Fetching manifest "' + this.manifestFile + '"... ');
553552

554553
if (this.delay > 0) {
555554
this._log("\nDelaying for " + this.delay + " ms...\n");
@@ -562,32 +561,39 @@ class Driver {
562561
this.ws.addEventListener("open", resolve, { once: true });
563562
});
564563
}
565-
const response = await fetch(this.manifestFile);
566-
if (!response.ok) {
567-
throw new Error(response.statusText);
568-
}
569-
this._log("done\n");
570-
this.manifest = await response.json();
571564

572-
if (this.testFilter?.length) {
573-
this.manifest = this.manifest.filter(item => {
574-
if (this.testFilter.includes(item.id)) {
575-
return true;
565+
// Dynamic task queue: server sends tasks on demand.
566+
this.taskQueue = [];
567+
this.serverDone = false;
568+
this.pendingTaskResolve = null;
569+
this.currentTask = null;
570+
this.tasksDone = 0;
571+
572+
this.ws.addEventListener("message", event => {
573+
if (typeof event.data !== "string") {
574+
return;
575+
}
576+
const msg = JSON.parse(event.data);
577+
if (msg.type === "task") {
578+
if (this.pendingTaskResolve) {
579+
this.pendingTaskResolve(msg.task);
580+
this.pendingTaskResolve = null;
581+
} else {
582+
this.taskQueue.push(msg.task);
583+
// Prefetch PDF for this task if it's now first in queue.
584+
if (this.taskQueue.length === 1) {
585+
this._prefetchNextTask();
586+
}
576587
}
577-
return false;
578-
});
579-
}
580-
if (this.sessionCount > 1) {
581-
const { sessionIndex, sessionCount } = this;
582-
const start = Math.floor(
583-
(this.manifest.length * sessionIndex) / sessionCount
584-
);
585-
const end = Math.floor(
586-
(this.manifest.length * (sessionIndex + 1)) / sessionCount
587-
);
588-
this.manifest = this.manifest.slice(start, end);
589-
}
590-
this.currentTask = 0;
588+
} else if (msg.type === "done") {
589+
this.serverDone = true;
590+
if (this.pendingTaskResolve) {
591+
this.pendingTaskResolve(null);
592+
this.pendingTaskResolve = null;
593+
}
594+
}
595+
});
596+
591597
this._nextTask();
592598
}, this.delay);
593599
}
@@ -602,23 +608,38 @@ class Driver {
602608
*/
603609
log(msg) {
604610
let id = this.browser;
605-
const task = this.manifest[this.currentTask];
606-
if (task) {
607-
id += `-${task.id}`;
611+
if (this.currentTask) {
612+
id += `-${this.currentTask.id}`;
608613
}
609-
610614
this._info(`${id}: ${msg}`);
611615
}
612616

617+
_waitForNextTask() {
618+
if (this.taskQueue.length > 0) {
619+
return Promise.resolve(this.taskQueue.shift());
620+
}
621+
if (this.serverDone) {
622+
return Promise.resolve(null);
623+
}
624+
this.ws.send(
625+
JSON.stringify({ type: "requestTask", browser: this.browser })
626+
);
627+
return new Promise(resolve => {
628+
this.pendingTaskResolve = resolve;
629+
});
630+
}
631+
613632
_nextTask() {
614633
let failure = "";
615634

616-
this._cleanup().then(() => {
617-
if (this.currentTask === this.manifest.length) {
635+
this._cleanup().then(async () => {
636+
const task = await this._waitForNextTask();
637+
if (!task) {
618638
this._done();
619639
return;
620640
}
621-
const task = this.manifest[this.currentTask];
641+
this.currentTask = task;
642+
622643
task.round = 0;
623644
task.pageNum = task.firstPage || 1;
624645
task.stats = { times: [] };
@@ -658,13 +679,10 @@ class Driver {
658679
md5FileMap.set(task.md5, task.file);
659680
}
660681

661-
this._log(
662-
`[${this.currentTask + 1}/${this.manifest.length}] ${task.id}:\n`
663-
);
682+
this._log(`[${++this.tasksDone}] ${task.id}:\n`);
664683

665684
if (task.type === "skip-because-failing") {
666685
this._log(` Skipping file "${task.file} because it's failing"\n`);
667-
this.currentTask++;
668686
this._nextTask();
669687
return;
670688
}
@@ -678,7 +696,6 @@ class Driver {
678696
this._nextPage(task, 'Expected "other" test-case to be linked.');
679697
return;
680698
}
681-
this.currentTask++;
682699
this._nextTask();
683700
return;
684701
}
@@ -885,11 +902,10 @@ class Driver {
885902
}
886903

887904
_prefetchNextTask() {
888-
const nextIdx = this.currentTask + 1;
889-
if (nextIdx >= this.manifest.length) {
905+
const task = this.taskQueue[0];
906+
if (!task) {
890907
return;
891908
}
892-
const task = this.manifest[nextIdx];
893909
// Skip tasks that do not load a PDF or that need DOM setup (XFA style
894910
// element injection) to happen synchronously before getDocument.
895911
if (
@@ -899,7 +915,9 @@ class Driver {
899915
) {
900916
return;
901917
}
902-
task._prefetchedLoadingTask = getDocument(this._getDocumentOptions(task));
918+
if (!task._prefetchedLoadingTask) {
919+
task._prefetchedLoadingTask = getDocument(this._getDocumentOptions(task));
920+
}
903921
}
904922

905923
_cleanup() {
@@ -918,10 +936,10 @@ class Driver {
918936

919937
const destroyedPromises = [];
920938
// Wipe out the link to the pdfdoc so it can be GC'ed.
921-
for (let i = 0; i < this.manifest.length; i++) {
922-
if (this.manifest[i].pdfDoc) {
923-
destroyedPromises.push(this.manifest[i].pdfDoc.destroy());
924-
delete this.manifest[i].pdfDoc;
939+
for (const task of [this.currentTask, ...this.taskQueue]) {
940+
if (task?.pdfDoc) {
941+
destroyedPromises.push(task.pdfDoc.destroy());
942+
delete task.pdfDoc;
925943
}
926944
}
927945
return Promise.all(destroyedPromises);
@@ -955,7 +973,6 @@ class Driver {
955973
.then(blob => this._sendResult(blob, task, failure))
956974
.then(() => {
957975
this._log(`done${failure ? ` (failed !: ${failure})` : ""}\n`);
958-
this.currentTask++;
959976
this._nextTask();
960977
});
961978
return;
@@ -966,7 +983,6 @@ class Driver {
966983
this._log(` Round ${1 + task.round}\n`);
967984
task.pageNum = task.firstPage || 1;
968985
} else {
969-
this.currentTask++;
970986
this._nextTask();
971987
return;
972988
}

0 commit comments

Comments
 (0)