tests: Vendor blink perf tests (#38654)

Vendors the [blink perf
tests](https://chromium.googlesource.com/chromium/src/+/HEAD/third_party/blink/perf_tests/).
These perf tests are useful to evaluate the performance of servo. 
The license that governs the perf tests is included in the folder. 
Running benchmark cases automatically is left to future work.

The update.py script is taken from mozjs and slightly adapted, so we can
easily filter
(and patch if this should be necessary in the future.

Testing: This PR just adds the perf_tests, but does not use or modify
them in any way.

---------

Signed-off-by: Jonathan Schwender <schwenderjonathan@gmail.com>
This commit is contained in:
Jonathan Schwender 2025-08-17 11:54:04 +02:00 committed by GitHub
parent 7621332824
commit ee781b71b4
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
648 changed files with 359694 additions and 0 deletions

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1 @@
{"type":3,"mutations":[{"target":{"_index_":18,"transferred":1},"addedNodes":null,"removedNodes":null,"previousSibling":null,"nextSibling":null,"attributeName":"class","attributeNamespace":null,"oldValue":"candidateTable_victor","type":0,"propertyName":null,"value":"candidateTable_victor candidateTable_displayVictor","addedEvents":null,"removedEvents":null,"measure":null},{"target":{"_index_":35,"transferred":1},"addedNodes":null,"removedNodes":null,"previousSibling":null,"nextSibling":null,"attributeName":"class","attributeNamespace":null,"oldValue":"candidateTable_victor candidateTable_displayVictor","type":0,"propertyName":null,"value":"candidateTable_victor","addedEvents":null,"removedEvents":null,"measure":null},{"target":{"_index_":74,"transferred":1},"addedNodes":null,"removedNodes":null,"previousSibling":null,"nextSibling":null,"attributeName":"fill","attributeNamespace":null,"oldValue":"black","type":0,"propertyName":null,"value":"red","addedEvents":null,"removedEvents":null,"measure":null},{"target":{"_index_":76,"transferred":1},"addedNodes":null,"removedNodes":null,"previousSibling":null,"nextSibling":null,"attributeName":null,"attributeNamespace":null,"oldValue":"Region winner Steven Armstrong","type":1,"propertyName":null,"value":"Region winner ","addedEvents":null,"removedEvents":null,"measure":null},{"target":{"_index_":75,"transferred":1},"addedNodes":null,"removedNodes":null,"previousSibling":null,"nextSibling":null,"attributeName":"class","attributeNamespace":null,"oldValue":"candidateMap_tooltip candidateMap_showTooltip","type":0,"propertyName":null,"value":"candidateMap_tooltip","addedEvents":null,"removedEvents":null,"measure":null}]}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,47 @@
const StructuredClonePerfTestRunner = (function() {
function pingPong(data) {
return new Promise((resolve, reject) => {
let beginSerialize, endSerialize, beginDeserialize, endDeserialize;
window.addEventListener('message', function listener(e) {
try {
e.data; // Force deserialization.
endDeserialize = PerfTestRunner.now();
window.removeEventListener('message', listener);
resolve([endSerialize - beginSerialize, endDeserialize - beginDeserialize]);
} catch (err) { reject(err); }
});
beginSerialize = PerfTestRunner.now();
window.postMessage(data, '*');
beginDeserialize = endSerialize = PerfTestRunner.now();
// While Chrome does the deserialize lazily when e.data is read, this
// isn't portable, so it's more fair to measure from when the message is
// posted.
});
}
return {
measureTimeAsync(test) {
let isDone = false;
PerfTestRunner.startMeasureValuesAsync({
description: test.description,
unit: 'ms',
warmUpCount: test.warmUpCount || 10,
iterationCount: test.iterationCount || 250,
done() { isDone = true; },
run: pingPongUntilDone,
});
function pingPongUntilDone() {
pingPong(test.data).then(([serializeTime, deserializeTime]) => {
console.log([serializeTime, deserializeTime]);
if (test.measure === 'serialize')
PerfTestRunner.measureValueAsync(serializeTime);
else if (test.measure === 'deserialize')
PerfTestRunner.measureValueAsync(deserializeTime);
if (!isDone) pingPongUntilDone();
});
}
},
};
})();

View file

@ -0,0 +1,45 @@
(module
(type $0 (func (param externref i32)))
(import "../clearCalls.js" "clearThruJS" (func $assembly/index/clearThruJS (param externref i32)))
(import "../clearCalls.js" "clearNoJS" (func $assembly/index/clearNoJS (param externref i32)))
(memory $0 0)
(export "clearManyTimesThruJS" (func $assembly/index/clearManyTimesThruJS))
(export "clearManyTimesNoJS" (func $assembly/index/clearManyTimesNoJS))
(export "memory" (memory $0))
(func $assembly/index/clearManyTimesThruJS (param $0 externref) (param $1 i32)
(local $2 i32)
loop $for-loop|0
local.get $1
local.get $2
i32.gt_s
if
local.get $0
i32.const 16384
call $assembly/index/clearThruJS
local.get $2
i32.const 1
i32.add
local.set $2
br $for-loop|0
end
end
)
(func $assembly/index/clearManyTimesNoJS (param $0 externref) (param $1 i32)
(local $2 i32)
loop $for-loop|0
local.get $1
local.get $2
i32.gt_s
if
local.get $0
i32.const 16384
call $assembly/index/clearNoJS
local.get $2
i32.const 1
i32.add
local.set $2
br $for-loop|0
end
end
)
)

View file

@ -0,0 +1,50 @@
const WorkerStructuredCloneDifferentPayloadsPerfTestRunner = (function() {
function pingPong(data) {
return new Promise((resolve, reject) => {
let mainThreadBeginSerialize, mainThreadEndDeserialize, iteration, numMessages;
iteration = 0;
numMessages = data['toWorker'].length;
worker.addEventListener('message', function listener(e) {
try {
e.data.sendData; // Force deserialization.
// keep sending messages to worker until worker runs out of responses.
if (!e.data.done && iteration < numMessages) {
iteration++;
worker.postMessage({'data' : data['toWorker'][iteration], 'iteration' : iteration});
} else {
mainThreadEndDeserialize = performance.now();
worker.removeEventListener('message', listener);
totalTime = mainThreadEndDeserialize - mainThreadBeginSerialize;
resolve([totalTime]);
}
} catch (err) { reject(err); }
});
mainThreadBeginSerialize = performance.now();
worker.postMessage({'data' : data['toWorker'][iteration], 'iteration' : iteration});
});
}
return {
measureTimeAsync(test) {
let isDone = false;
worker = new Worker('resources/worker-structured-clone-different-payloads.js');
PerfTestRunner.startMeasureValuesAsync({
description: test.description,
unit: 'ms',
warmUpCount: test.warmUpCount || 5,
iterationCount: test.iterationCount || 15,
done() { isDone = true; },
run: pingPongUntilDone,
});
function pingPongUntilDone() {
pingPong(test.data).then(([totalTime]) => {
console.log([totalTime]);
if (test.measure == 'roundtrip')
PerfTestRunner.measureValueAsync(totalTime);
if (!isDone) pingPongUntilDone();
});
}
},
};
})();

View file

@ -0,0 +1,15 @@
var xhr = new XMLHttpRequest();
xhr.open("GET", "data/preact-from-worker.json", false);
xhr.send(null);
var sendData = JSON.parse(xhr.responseText)
var length = sendData['fromWorker'].length;
self.onmessage = function(e) {
var data = e.data; // Force deserialization.
var iteration = e.data.iteration;
var done = false;
if (iteration == length - 1){
done = true;
}
self.postMessage({'sendData' : sendData['fromWorker'][iteration], 'done': done});
};

View file

@ -0,0 +1,51 @@
const WorkerStructuredClonePerfTestRunner = (function() {
function pingPong(data) {
return new Promise((resolve, reject) => {
let mainThreadBeginSerialize, mainThreadEndDeserialize, toWorkerTime, fromWorkerTime, totalTime;
worker.addEventListener('message', function listener(e) {
try {
e.data; // Force deserialization.
mainThreadEndDeserialize = performance.now();
worker.removeEventListener('message', listener);
// toWorkerTime: Time from main thread beginning serialize to end of worker deserialize
toWorkerTime = (e.data.workerDeserialize + (e.data.workerTimeOrigin - performance.timeOrigin)) - mainThreadBeginSerialize;
// fromWorkerTime: Time from worker beginning serialize to end of main thread deserialize
fromWorkerTime = mainThreadEndDeserialize - (e.data.workerDeserialize + (e.data.workerTimeOrigin - performance.timeOrigin));
// totalTime: Time from main thread beginning serialzie to end of main thread deserialize
totalTime = mainThreadEndDeserialize - mainThreadBeginSerialize
resolve([toWorkerTime, fromWorkerTime, totalTime]);
} catch (err) { reject(err); }
});
mainThreadBeginSerialize = performance.now();
worker.postMessage(data);
});
}
return {
measureTimeAsync(test) {
let isDone = false;
worker = new Worker('resources/worker-structured-clone.js');
PerfTestRunner.startMeasureValuesAsync({
description: test.description,
unit: 'ms',
warmUpCount: test.warmUpCount || 10,
iterationCount: test.iterationCount || 250,
done() { isDone = true; },
run: pingPongUntilDone,
});
function pingPongUntilDone() {
pingPong(test.data).then(([toWorkerTime, fromWorkerTime, totalTime]) => {
console.log([toWorkerTime, fromWorkerTime, totalTime]);
if (test.measure == 'toWorker')
PerfTestRunner.measureValueAsync(toWorkerTime);
else if (test.measure === 'fromWorker')
PerfTestRunner.measureValueAsync(fromWorkerTime);
else if (test.measure == 'roundtrip')
PerfTestRunner.measureValueAsync(totalTime);
if (!isDone) pingPongUntilDone();
});
}
},
};
})();

View file

@ -0,0 +1,7 @@
self.onmessage = function(e) {
var data = e.data; // Force deserialization
var workerDeserialize = performance.now();
self.postMessage({'recieveddData' : data,
'workerTimeOrigin' : performance.timeOrigin,
'workerDeserialize' : workerDeserialize});
};

View file

@ -0,0 +1,54 @@
const WorkerTextEncodedTransferablePerfTestRunner = (function() {
function pingPong(data) {
return new Promise((resolve, reject) => {
let sendData, textEncoder, textDecoder, mainThreadBeginEncode, mainThreadEndDecode, toWorkerTime, fromWorkerTime, totalTime;
textEncoder = new TextEncoder('utf-8');
textDecoder = new TextDecoder('utf-8');
worker.addEventListener('message', function listener(e) {
try {
textDecoder.decode(e.data.data);
mainThreadEndDecode = performance.now();
worker.removeEventListener('message', listener);
// toWorkerTime: time to encode the data, send it to the worker, and decode it on the worker
toWorkerTime = (e.data.workerDecode + (e.data.workerTimeOrigin - performance.timeOrigin)) - mainThreadBeginEncode;
// fromWorkerTime: time to encode the data on the worker, send it back to the main thread, and deocde it
fromWorkerTime = mainThreadEndDecode - (e.data.workerDecode + (e.data.workerTimeOrigin - performance.timeOrigin));
// totalTime: time to do the whole roundtrip
totalTime = mainThreadEndDecode - mainThreadBeginEncode
resolve([toWorkerTime, fromWorkerTime, totalTime]);
} catch (err) { reject(err); }
});
mainThreadBeginEncode = performance.now();
sendData = textEncoder.encode(data).buffer;
worker.postMessage({"data" : sendData}, [sendData]);
});
}
return {
measureTimeAsync(test) {
let isDone = false;
worker = new Worker('resources/worker-text-encoded-transferable.js');
PerfTestRunner.startMeasureValuesAsync({
description: test.description,
unit: 'ms',
warmUpCount: test.warmUpCount || 10,
iterationCount: test.iterationCount || 250,
done() { isDone = true; },
run: pingPongUntilDone,
});
function pingPongUntilDone() {
pingPong(test.data).then(([toWorkerTime, fromWorkerTime, totalTime]) => {
console.log([toWorkerTime, fromWorkerTime, totalTime]);
if (test.measure == 'toWorker')
PerfTestRunner.measureValueAsync(toWorkerTime);
else if (test.measure === 'fromWorker')
PerfTestRunner.measureValueAsync(fromWorkerTime);
else if (test.measure == 'roundtrip')
PerfTestRunner.measureValueAsync(totalTime);
if (!isDone) pingPongUntilDone();
});
}
},
};
})();

View file

@ -0,0 +1,11 @@
var textDecoder = new TextDecoder('utf-8');
var textEncoder = new TextEncoder('utf-8');
self.onmessage = function(e) {
var data = textDecoder.decode(e.data.data);
var workerDecode = performance.now();
var sendData = textEncoder.encode(data).buffer;
self.postMessage({'data' : sendData,
'workerTimeOrigin' : performance.timeOrigin,
'workerDecode' : workerDecode},
[sendData]);
};

View file

@ -0,0 +1,52 @@
const WorkerTransferablePerfTestRunner = (function() {
function pingPong(data) {
return new Promise((resolve, reject) => {
let sendData, mainThreadBeginTransfer, workerEndTransfer, toWorkerTime, fromWorkerTime, totalTime;
worker.addEventListener('message', function listener(e) {
try {
e.data.data;
workerEndTransfer = performance.now();
worker.removeEventListener('message', listener);
// toWorkerTime: Time for the main thread to transfer data to the worker
toWorkerTime = (e.data.mainThreadEndTransfer + (e.data.workerTimeOrigin - performance.timeOrigin)) - mainThreadBeginTransfer;
// fromWorkerTime: Time for the worker to transfer data back to the main thread
fromWorkerTime = workerEndTransfer - (e.data.mainThreadEndTransfer + (e.data.workerTimeOrigin - performance.timeOrigin));
// totalTime: Time from main thread beginning transfer to the end of the worker transfer
totalTime = workerEndTransfer - mainThreadBeginTransfer
resolve([toWorkerTime, fromWorkerTime, totalTime]);
} catch (err) { reject(err); }
});
sendData = data.slice(0); // Copy the data for every new transfer
mainThreadBeginTransfer = performance.now();
worker.postMessage({"data" : sendData}, [sendData]);
});
}
return {
measureTimeAsync(test) {
let isDone = false;
worker = new Worker('resources/worker-transferable.js');
PerfTestRunner.startMeasureValuesAsync({
description: test.description,
unit: 'ms',
warmUpCount: test.warmUpCount || 10,
iterationCount: test.iterationCount || 250,
done() { isDone = true;},
run: pingPongUntilDone,
});
function pingPongUntilDone() {
pingPong(test.data).then(([toWorkerTime, fromWorkerTime, totalTime]) => {
console.log([toWorkerTime, fromWorkerTime, totalTime]);
if (test.measure == 'toWorker')
PerfTestRunner.measureValueAsync(toWorkerTime);
else if (test.measure === 'fromWorker')
PerfTestRunner.measureValueAsync(fromWorkerTime);
else if (test.measure == 'roundtrip')
PerfTestRunner.measureValueAsync(totalTime);
if (!isDone) pingPongUntilDone();
});
}
},
};
})();

View file

@ -0,0 +1,8 @@
self.onmessage = function(e) {
var data = e.data.data;
var mainThreadEndTransfer = performance.now();
self.postMessage({'data' : data,
'workerTimeOrigin' : performance.timeOrigin,
'mainThreadEndTransfer' : mainThreadEndTransfer},
[data]);
};

View file

@ -0,0 +1,3 @@
self.onmessage = function(m) {
self.postMessage('received');
};