Commit 7fe30be9 authored by Hongchan Choi's avatar Hongchan Choi Committed by Commit Bot

WebAudio performance benchmark

This CL adds the initial set of benchmark tests designed for Web
Audio API. For the technical detail, see go/webaudio-perf
(googler-only).

Bug: 1052424
Change-Id: Id1ff5ac7c3a197f49e78965478f78fd2a82fa073
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2064824Reviewed-by: default avatarRaymond Toy <rtoy@chromium.org>
Reviewed-by: default avatarJohn Chen <johnchen@chromium.org>
Reviewed-by: default avatarKentaro Hara <haraken@chromium.org>
Commit-Queue: Hongchan Choi <hongchan@chromium.org>
Cr-Commit-Position: refs/heads/master@{#758712}
parent 8ab26431
<!DOCTYPE html>
<html>
<head>
<title>
Test performance of 100 AudioBufferSourceNodes.
</title>
<script src="../resources/runner.js"></script>
<script src="resources/webaudio-perf-utils.js"></script>
</head>
<body>
<script>
function graphBuilder() {
const context = new OfflineAudioContext(1, 48000, 48000);
const buffer = createMonoRampBuffer(4800, 48000);
const testNodes =
createNodes(context, 'AudioBufferSourceNode', 100, {buffer});
// All the test nodes fan-in to the destination node.
testNodes.forEach(node => {
node.connect(context.destination);
node.start();
});
return context;
}
RunWebAudioPerfTest({
description: 'Test performance of 100 AudioBufferSourceNodes',
graphBuilder: graphBuilder,
tracingOptions: {
targetCategory: 'disabled-by-default-webaudio.audionode',
targetEvents: ['AudioBufferSourceHandler::Process'],
}
});
</script>
</body>
</html>
<!DOCTYPE html>
<html>
<head>
<title>
Test performance of 100 AudioWorkletNodes (bypassing).
</title>
<script src="../resources/runner.js"></script>
<script src="resources/webaudio-perf-utils.js"></script>
</head>
<body>
<script>
async function graphBuilder() {
const context = new OfflineAudioContext(1, 48000, 48000);
await context.audioWorklet.addModule('resources/bypass-processor.js');
const source = new ConstantSourceNode(context);
// Create 100 AudioWorkletNodes that are serially connected.
const testNodes = [];
for (let i = 0; i < 100; ++i) {
testNodes.push(new AudioWorkletNode(context, 'bypass-processor'));
if (i === 0) continue;
testNodes[i - 1].connect(testNodes[i]);
}
source.connect(testNodes[0]);
testNodes[99].connect(context.destination);
source.start();
return context;
}
RunWebAudioPerfTest({
description: 'Test performance of 100 AudioWorkletNodes (bypassing)',
graphBuilder: graphBuilder,
tracingOptions: {
targetCategory: 'disabled-by-default-webaudio.audionode',
targetEvents: ['AudioWorkletHandler::Process'],
}
});
</script>
</body>
</html>
<!DOCTYPE html>
<html>
<head>
<title>
Test performance of 100 BiquadFilterNodes.
</title>
<script src="../resources/runner.js"></script>
<script src="resources/webaudio-perf-utils.js"></script>
</head>
<body>
<script>
function graphBuilder() {
const context = new OfflineAudioContext(1, 48000, 48000);
const source = new ConstantSourceNode(context);
const testNodes =
createAndConnectNodesInSeries(context, 'BiquadFilterNode', 100);
source.connect(testNodes.head);
testNodes.tail.connect(context.destination);
source.start();
return context;
}
RunWebAudioPerfTest({
description: 'Test performance of 100 BiquadFilterNodes',
graphBuilder: graphBuilder,
tracingOptions: {
targetCategory: 'disabled-by-default-webaudio.audionode',
targetEvents: ['BiquadFilterHandler::Process'],
}
});
</script>
</body>
</html>
\ No newline at end of file
<!DOCTYPE html>
<html>
<head>
<title>
Test performance of 100 GainNodes.
</title>
<script src="../resources/runner.js"></script>
<script src="resources/webaudio-perf-utils.js"></script>
</head>
<body>
<script>
function graphBuilder() {
const context = new OfflineAudioContext(1, 48000, 48000);
const source = new ConstantSourceNode(context);
const testNodes = createAndConnectNodesInSeries(
context, 'GainNode', 100, {gain: 0.707});
source.connect(testNodes.head);
testNodes.tail.connect(context.destination);
source.start();
return context;
}
RunWebAudioPerfTest({
description: 'Test performance of 100 GainNodes',
graphBuilder: graphBuilder,
tracingOptions: {
targetCategory: 'disabled-by-default-webaudio.audionode',
targetEvents: ['GainHandler::Process'],
}
});
</script>
</body>
</html>
<!DOCTYPE html>
<html>
<head>
<title>
Test performance of 100 PannerNodes (HRTF).
</title>
<script src="../resources/runner.js"></script>
<script src="resources/webaudio-perf-utils.js"></script>
</head>
<body>
<script>
function graphBuilder() {
const context = new OfflineAudioContext(1, 48000, 48000);
const source = new ConstantSourceNode(context);
const testNodes = createAndConnectNodesInSeries(
context, 'PannerNode', 100, {panningModel: 'HRTF'});
source.connect(testNodes.head);
testNodes.tail.connect(context.destination);
source.start();
return context;
}
RunWebAudioPerfTest({
description: 'Test performance of 100 PannerNodes (HRTF)',
graphBuilder: graphBuilder,
tracingOptions: {
targetCategory: 'disabled-by-default-webaudio.audionode',
targetEvents: ['PannerHandler::Process'],
}
});
</script>
</body>
</html>
/**
* @class BypassProcessor
* @extends AudioWorkletProcessor
*/
class BypassProcessor extends AudioWorkletProcessor {
process(inputs, outputs) {
let input = inputs[0];
let output = outputs[0];
for (let channel = 0; channel < input.length; ++channel)
output[channel].set(input[channel]);
return true;
}
}
registerProcessor('bypass-processor', BypassProcessor);
\ No newline at end of file
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
/**
* Set up and perform a test with given test options.
*
* @param {!object} testOptions
* @param {string} testOptions.description test description
* @param {function} testOptions.graphBuilder a test function returns an
* OfflineAudioContext.
* @param {object} testOptions.tracingOptions test options
* @param {string} testOptions.tracingOptions.targetCategory
* target trace category
* @param {Array<string>} testOptions.tracingOptions.targetEvents
* target trace events
*/
function RunWebAudioPerfTest(testOptions) {
let isDone = false;
let startTime;
async function runTest_internal() {
const context = await testOptions.graphBuilder();
PerfTestRunner.addRunTestStartMarker();
startTime = PerfTestRunner.now();
await context.startRendering();
PerfTestRunner.measureValueAsync(PerfTestRunner.now() - startTime);
PerfTestRunner.addRunTestEndMarker();
if (!isDone)
runTest_internal();
}
PerfTestRunner.startMeasureValuesAsync({
unit: 'ms',
description: testOptions.description,
done: () => isDone = true,
run: runTest_internal,
warmUpCount: 2,
iterationCount: 5,
tracingCategories: testOptions.tracingOptions.targetCategory,
traceEventsToMeasure: testOptions.tracingOptions.targetEvents,
});
}
/**
* Creates multiple AudioNodes that are serially connected
*
* @param {BaseAudioContext} context
* @param {string} nodeName AudioNode name in string
* @param {number} numberOfNodes
* @param {AudioNodeOptions} nodeOptions
* @returns {object}
*/
function createAndConnectNodesInSeries(
context, nodeName, numberOfNodes, nodeOptions) {
const testNodes = [];
nodeOptions = nodeOptions || {};
for (let i = 0; i < numberOfNodes; ++i) {
testNodes.push(new window[nodeName](context, nodeOptions));
if (i === 0) continue;
testNodes[i - 1].connect(testNodes[i]);
}
return {
head: testNodes[0],
tail: testNodes[numberOfNodes - 1],
nodes: testNodes,
};
}
/**
* Creates multiple AudioNodes.
*
* @param {BaseAudioContext} context
* @param {string} nodeName AudioNode name in string
* @param {number} numberOfNodes
* @param {AudioNodeOptions} nodeOptions
* @returns {Array<AudioNode>}
*/
function createNodes(context, nodeName, numberOfNodes, nodeOptions) {
const testNodes = [];
nodeOptions = nodeOptions || {};
for (let i = 0; i < numberOfNodes; ++i)
testNodes.push(new window[nodeName](context, nodeOptions));
return testNodes;
}
/**
* Creates an AudioBuffer with up-ramp samples.
*
* @param {number} length number of samples
* @param {number} sampleRate sample rate
* @returns {AudioBuffer}
*/
function createMonoRampBuffer(length, sampleRate) {
let buffer = new AudioBuffer({numberOfChannel: 1, length, sampleRate});
let channelData = buffer.getChannelData(0);
for (let i = 0; i < length; ++i)
channelData[i] = i / length;
return buffer;
}
...@@ -3,6 +3,7 @@ See the following link for directions for making changes to this data:,https://b ...@@ -3,6 +3,7 @@ See the following link for directions for making changes to this data:,https://b
Googlers can view additional information about internal perf infrastructure at,https://goto.google.com/chrome-benchmarking-sheet Googlers can view additional information about internal perf infrastructure at,https://goto.google.com/chrome-benchmarking-sheet
Benchmark name,Individual owners,Component,Documentation,Tags Benchmark name,Individual owners,Component,Documentation,Tags
UNSCHEDULED_blink_perf.service_worker,"shimazu@chromium.org, falken@chromium.org, ting.shao@intel.com",Blink>ServiceWorker,https://bit.ly/blink-perf-benchmarks, UNSCHEDULED_blink_perf.service_worker,"shimazu@chromium.org, falken@chromium.org, ting.shao@intel.com",Blink>ServiceWorker,https://bit.ly/blink-perf-benchmarks,
UNSCHEDULED_blink_perf.webaudio,"hongchan@chromium.org, rtoy@chromium.org",Blink>WebAudio,https://bit.ly/blink-perf-benchmarks,all
angle_perftests,"jmadill@chromium.org, chrome-gpu-perf-owners@chromium.org",Internals>GPU>ANGLE,, angle_perftests,"jmadill@chromium.org, chrome-gpu-perf-owners@chromium.org",Internals>GPU>ANGLE,,
base_perftests,"skyostil@chromium.org, gab@chromium.org",Internals>SequenceManager,https://chromium.googlesource.com/chromium/src/+/HEAD/base/README.md#performance-testing, base_perftests,"skyostil@chromium.org, gab@chromium.org",Internals>SequenceManager,https://chromium.googlesource.com/chromium/src/+/HEAD/base/README.md#performance-testing,
blink_perf.accessibility,dmazzoni@chromium.org,Blink>Accessibility,https://bit.ly/blink-perf-benchmarks,all blink_perf.accessibility,dmazzoni@chromium.org,Blink>Accessibility,https://bit.ly/blink-perf-benchmarks,all
......
...@@ -628,3 +628,14 @@ class BlinkPerfDisplayLocking(_BlinkPerfBenchmark): ...@@ -628,3 +628,14 @@ class BlinkPerfDisplayLocking(_BlinkPerfBenchmark):
def SetExtraBrowserOptions(self, options): def SetExtraBrowserOptions(self, options):
options.AppendExtraBrowserArgs( options.AppendExtraBrowserArgs(
['--enable-blink-features=DisplayLocking,CSSContentSize']) ['--enable-blink-features=DisplayLocking,CSSContentSize'])
@benchmark.Info(emails=['hongchan@chromium.org', 'rtoy@chromium.org'],
component='Blink>WebAudio',
documentation_url='https://bit.ly/blink-perf-benchmarks')
class BlinkPerfWebAudio(_BlinkPerfBenchmark):
SUBDIR = 'webaudio'
TAGS = _BlinkPerfBenchmark.TAGS + ['all']
@classmethod
def Name(cls):
return 'UNSCHEDULED_blink_perf.webaudio'
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment