[[PageOutline]] = What is a Performance Test = A performance test measures the run-time performance and memory usage of WebKit. Unlike [http://www.webkit.org/quality/testing.html regression tests] (a.k.a layout tests) or conformance tests such as of W3C's, it doesn't necessarily test the correctness of WebKit features. Since the run-time and memory used by each run of the same test may vary, we can't conclude whether a given performance test passed or failed by just looking at a single run. For this reason, performance tests yields "values" such as the time taken to run the test instead of simple PASS and FAIL. = Performance Test Results = We have continuous performance test bots on [http://build.webkit.org/waterfall?show=Apple%20MountainLion%20Release%20%28Perf%29&show=Apple%20Mavericks%20Release%20%28Perf%29&show=EFL%20Linux%2064-bit%20Release%20WK2%20%28Perf%29&show=GTK%20Linux%2064-bit%20Release%20%28Perf%29 build.webkit.org]. You can see test results submitted by these bots on http://perf.webkit.org/ {{{#!td style="padding: 1em;" ||''' The waterfall of the Performance bots on the [http://build.webkit.org/waterfall?show=Apple%20MountainLion%20Release%20%28Perf%29&show=Apple%20Mavericks%20Release%20%28Perf%29&show=EFL%20Linux%2064-bit%20Release%20WK2%20%28Perf%29&show=GTK%20Linux%2064-bit%20Release%20%28Perf%29 Buildbot page]''' || '''Platform name on the [http://perf.webkit.org results page]''' || || [http://build.webkit.org/waterfall?show=Apple%20Mavericks%20Release%20%28Perf%29 Apple Mavericks Release (Perf)] || [https://perf.webkit.org/#mode=charts&chartList=%5b%5b%22mac-mavericks%22%2C%22DoYouEvenBench%2FFull%3ATime%3ATotal%22%5d%2C%5b%22mac-mavericks%22%2C%22Parser%2Fhtml5-full-render%3ATime%22%5d%5d mac-mavericks] || || [http://build.webkit.org/waterfall?show=Apple%20MountainLion%20Release%20%28Perf%29 Apple MountainLion Release (Perf)] || [https://perf.webkit.org/#mode=charts&chartList=%5b%5b%22mac-mountainlion%22%2C%22DoYouEvenBench%2FFull%3ATime%3ATotal%22%5d%2C%5b%22mac-mountainlion%22%2C%22Parser%2Fhtml5-full-render%3ATime%22%5d%5d mac-mountainlion] || || [http://build.webkit.org/waterfall?show=EFL%20Linux%2064-bit%20Release%20WK2%20%28Perf%29 EFL Linux 64-bit Release WK2 (Perf)] || [https://perf.webkit.org/#mode=charts&chartList=%5b%5b%22efl%22%2C%22DoYouEvenBench%2FFull%3ATime%3ATotal%22%5d%2C%5b%22efl%22%2C%22Parser%2Fhtml5-full-render%3ATime%22%5d%5d efl] || || [http://build.webkit.org/waterfall?show=GTK%20Linux%2064-bit%20Release%20%28Perf%29 GTK Linux 64-bit Release (Perf)] || [https://perf.webkit.org/#mode=charts&chartList=%5b%5b%22gtk%22%2C%22DoYouEvenBench%2FFull%3ATime%3ATotal%22%5d%2C%5b%22gtk%22%2C%22Parser%2Fhtml5-full-render%3ATime%22%5d%5d gtk] || }}} = How to Run Performance Tests = WebKit's performance tests can be run by [http://trac.webkit.org/browser/trunk/Tools/Scripts/run-perf-tests run-perf-tests]. Specify a list of directories or performance tests to run a subset. e.g. {{{run-perf-tests PerformanceTests/DOM}}} or {{{run-perf-tests DOM}}} will only run tests in [http://trac.webkit.org/browser/trunk/PerformanceTests/DOM]. It will automatically build DumpRenderTree and WebKitTestRunner as needed just like {{{run-webkit-tests}}}. == Reducing noise on your machine == Before running performance tests, you may want to reboot your machine, disable screen savers and power saving features, and turn off anti-virus software to reduce the potential noise. Also disable network, bluetooth, and other network and wireless devices as they might cause undesirable CPU interrupts and context switches. On Mac, you can run the following command to disable Spotlight: {{{ sudo launchctl unload -w /System/Library/LaunchDaemons/com.apple.metadata.mds.plist }}} (To re-enable, run the same command with {{{load}}} in place of {{{unload}}}) == Aggregating and Comparing Results == If you're running a performance test locally in order to verify your patch doesn't regress or improves the performance of WebKit, you may find {{{--output-json-path}}} useful. Specify a file path such as {{{perf-test.json}}} and {{{run-perf-tests}}} will automatically store the results in the JSON file and creates {{{perf-test.html}}} that visualizes the test results. Execute {{{run-perf-tests}}} multiple times with the same output JSON path and it will automatically aggregate results in the JSON and the corresponding HTML document. Suppose we have two WebKit checkouts: one without a patch and another one with the patch applied. By executing {{{run-perf-tests --output-json-path=/Users/WebKitten/perf-test.json}}} in both checkouts, we can easily compare the test results from two runs by opening {{{~/perf-test.html}}}. You can also specify a build directory as follows along with the output JSON path: {{{ run-perf-tests --no-build --build-directory /Users/WebKitten/MyCustomBuild/ --output-json-path=/Users/WebKitten/perf-test.json }}} This allows you to compare results from different builds without having to locally build DumpRenderTree or WebKitTestRunner. === Bisecting regressions === Suppose you're bisecting a regression for a performance regression on [http://trac.webkit.org/browser/trunk/PerformanceTests/Bindings/node-list-access.html Bindings/node-list-access.html] as seen [http://webkit-perf.appspot.com/graph.html#tests=%5B%5B2966378%2C2001%2C32196%5D%5D&sel=1343895254495.4062,1344094768078.8474,116.85483870967741,148.30645161290323&displayrange=30&datatype=running here]. Looking at the graph we see that the culprit lies [http://trac.webkit.org/log/?rev=124582&stop_rev=124567&verbose=on "between r124567 and r124582"]. To bisect this regression, I create two WebKit checkouts one synced to r124567 and another synced to r124582, and run the following commands in each checkout: {{{ svn up PerformanceTests svn up Tools/Scripts/webkitpy/performance_tests Tools/Scripts/build-webkit Tools/Scripts/run-perf-tests --output-json-path=/Users/WebKitten/Desktop/node-list-access.json PerformanceTests/Bindings/node-list-access.html }}} This step automatically produces {{{/Users/WebKitten/Desktop/node-list-access.html}}} for me to compare the results, each results labeled r124567 and r124582 (you can use {{{--description}}} option to annotate the results further) and I can confirm whether the regression reproduces locally or not. Sometimes, regression doesn't produce on your local machine due to differences in environment such as compilers used, memory size, and CPU speed. Once I confirmed that the regression is reproducible on my machine, I can start bisecting builds. Here, I sync the checkout initially synced to r124582 to a slightly older version, say, r124580 and generate results again as follows: {{{ svn up -r 124580 svn up PerformanceTests svn up Tools/Scripts/webkitpy/performance_tests Tools/Scripts/build-webkit Tools/Scripts/run-perf-tests --output-json-path=/Users/WebKitten/Desktop/node-list-access.json PerformanceTests/Bindings/node-list-access.html }}} I repeat this process until the results recovers to the level we had at r124567, at which I identified the culprit. I don't typically do a strict binary search on perf. regressions because that typically results to avoid rebuilding the entire WebKit all the time. = Writing a Performance Test Using runner.js = The easiest way to write a performance test is using [http://trac.webkit.org/browser/trunk/PerformanceTests/resources/runner.js runner.js], which provides {{{PerfTestRunner}}} with various utility functions. Once you wrote a test, put it inside [http://trac.webkit.org/browser/trunk/PerformanceTests PerformanceTests] directory to be ran by run-perf-tests and performance bots. == Measuring Runs Per Second == Our preferred method of measurement is runs (function calls) per second. With runner.js, we can measure this metric by calling {{{PerfTestRunner.measureRunsPerSecond}}} with a test function. {{{PerfTestRunner.measureRunsPerSecond}}} measures the time of times {{{run}}} function could be called in one second, and reports the statistics after repeating it 20 times (configurable via {{{run-perf-tests}}}). The statistics includes arithmetic mean, standard deviation, median, minimum, and maximum values. For example, see [http://trac.webkit.org/browser/trunk/PerformanceTests/Parser/tiny-innerHTML.html Parser/tiny-innerHTML.html]: {{{ }}} == Measuring Time == In some tests, however, we cannot call the {{{run}}} function for an arbitrary number of times as done in {{{measureRunsPerSecond}}}. In those tests, we can use {{{PerfTestRunner.measureTime}}} to measure the time {{{run}}} took to execute. {{{measureTime}}} calls the specified function once in each iteration and runs 20 iterations by default. Note that the runtime of a function gets smaller relative to the granularity of time measurement we can make as the WebKit's performance (or of the machines that run performance tests) improves. == Measuring Asynchronous Results == In some tests such as ones that measure fps, values are measured asynchronously. In those tests, we can use {{{PerfTestRunner.prepareToMeasureValuesAsync}}} and {{{PerfTestRunner.measureValueAsync}}} to report measured value at an arbitrary time. At the beginning of a test, call {{{PerfTestRunner.prepareToMeasureValuesAsync}}} with an object with {{{unit}}} property, which specifies the name of the unit (either one of "ms", "fps", or "runs/s"). Call {{{PerfTestRunner.measureValueAsync}}} as a newly measured value comes in. Once enough values are measured (20 by default), {{{PerfTestRunner.measureValueAsync}}} will automatically stop the test; do not expect or manually track the number of iterations in your test as this must be configurable via {{{run-perf-tests}}}. For example, see [http://trac.webkit.org/browser/trunk/PerformanceTests/Interactive/SelectAll.html Interactive/SelectAll.html]: {{{