1*3ac0a46fSAndroid Build Coastguard Worker# SafetyNet - Performance regression detection for PDFium 2*3ac0a46fSAndroid Build Coastguard Worker 3*3ac0a46fSAndroid Build Coastguard Worker[TOC] 4*3ac0a46fSAndroid Build Coastguard Worker 5*3ac0a46fSAndroid Build Coastguard WorkerThis document explains how to use SafetyNet to detect performance regressions 6*3ac0a46fSAndroid Build Coastguard Workerin PDFium. 7*3ac0a46fSAndroid Build Coastguard Worker 8*3ac0a46fSAndroid Build Coastguard Worker## Comparing performance of two versions of PDFium 9*3ac0a46fSAndroid Build Coastguard Worker 10*3ac0a46fSAndroid Build Coastguard Workersafetynet_compare.py is a script that compares the performance between two 11*3ac0a46fSAndroid Build Coastguard Workerversions of pdfium. This can be used to verify if a given change has caused 12*3ac0a46fSAndroid Build Coastguard Workeror will cause any positive or negative changes in performance for a set of test 13*3ac0a46fSAndroid Build Coastguard Workercases. 14*3ac0a46fSAndroid Build Coastguard Worker 15*3ac0a46fSAndroid Build Coastguard WorkerThe supported profilers are exclusive to Linux, so for now this can only be run 16*3ac0a46fSAndroid Build Coastguard Workeron Linux. 17*3ac0a46fSAndroid Build Coastguard Worker 18*3ac0a46fSAndroid Build Coastguard WorkerAn illustrative example is below, comparing the local code version to an older 19*3ac0a46fSAndroid Build Coastguard Workerversion. Positive % changes mean an increase in time/instructions to run the 20*3ac0a46fSAndroid Build Coastguard Workertest - a regression, while negative % changes mean a decrease in 21*3ac0a46fSAndroid Build Coastguard Workertime/instructions, therefore an improvement. 22*3ac0a46fSAndroid Build Coastguard Worker 23*3ac0a46fSAndroid Build Coastguard Worker``` 24*3ac0a46fSAndroid Build Coastguard Worker$ testing/tools/safetynet_compare.py ~/test_pdfs --branch-before beef5e4 25*3ac0a46fSAndroid Build Coastguard Worker================================================================================ 26*3ac0a46fSAndroid Build Coastguard Worker % Change Time after Test case 27*3ac0a46fSAndroid Build Coastguard Worker-------------------------------------------------------------------------------- 28*3ac0a46fSAndroid Build Coastguard Worker -0.1980% 45,703,820,326 ~/test_pdfs/PDF Reference 1-7.pdf 29*3ac0a46fSAndroid Build Coastguard Worker -0.5678% 42,038,814 ~/test_pdfs/Page 24 - PDF Reference 1-7.pdf 30*3ac0a46fSAndroid Build Coastguard Worker +0.2666% 10,983,158,809 ~/test_pdfs/Rival.pdf 31*3ac0a46fSAndroid Build Coastguard Worker +0.0447% 10,413,890,748 ~/test_pdfs/dynamic.pdf 32*3ac0a46fSAndroid Build Coastguard Worker -7.7228% 26,161,171 ~/test_pdfs/encrypted1234.pdf 33*3ac0a46fSAndroid Build Coastguard Worker -0.2763% 102,084,398 ~/test_pdfs/ghost.pdf 34*3ac0a46fSAndroid Build Coastguard Worker -3.7005% 10,800,642,262 ~/test_pdfs/musician.pdf 35*3ac0a46fSAndroid Build Coastguard Worker -0.2266% 45,691,618,789 ~/test_pdfs/no_metadata.pdf 36*3ac0a46fSAndroid Build Coastguard Worker +1.4440% 38,442,606,162 ~/test_pdfs/test7.pdf 37*3ac0a46fSAndroid Build Coastguard Worker +0.0335% 9,286,083 ~/test_pdfs/testbulletpoint.pdf 38*3ac0a46fSAndroid Build Coastguard Worker================================================================================ 39*3ac0a46fSAndroid Build Coastguard WorkerTest cases run: 10 40*3ac0a46fSAndroid Build Coastguard WorkerFailed to measure: 0 41*3ac0a46fSAndroid Build Coastguard WorkerRegressions: 0 42*3ac0a46fSAndroid Build Coastguard WorkerImprovements: 2 43*3ac0a46fSAndroid Build Coastguard Worker``` 44*3ac0a46fSAndroid Build Coastguard Worker 45*3ac0a46fSAndroid Build Coastguard Worker### Usage 46*3ac0a46fSAndroid Build Coastguard Worker 47*3ac0a46fSAndroid Build Coastguard WorkerRun the safetynet_compare.py script in testing/tools to perform a comparison. 48*3ac0a46fSAndroid Build Coastguard WorkerPass one or more paths with test cases - each path can be either a .pdf file or 49*3ac0a46fSAndroid Build Coastguard Workera directory containing .pdf files. Other files in those directories are 50*3ac0a46fSAndroid Build Coastguard Workerignored. 51*3ac0a46fSAndroid Build Coastguard Worker 52*3ac0a46fSAndroid Build Coastguard WorkerThe following comparison modes are supported: 53*3ac0a46fSAndroid Build Coastguard Worker 54*3ac0a46fSAndroid Build Coastguard Worker1. Compare uncommitted changes against clean branch: 55*3ac0a46fSAndroid Build Coastguard Worker```shell 56*3ac0a46fSAndroid Build Coastguard Worker$ testing/tools/safetynet_compare.py path/to/pdfs 57*3ac0a46fSAndroid Build Coastguard Worker``` 58*3ac0a46fSAndroid Build Coastguard Worker 59*3ac0a46fSAndroid Build Coastguard Worker2. Compare current branch with another branch or commit: 60*3ac0a46fSAndroid Build Coastguard Worker```shell 61*3ac0a46fSAndroid Build Coastguard Worker$ testing/tools/safetynet_compare.py path/to/pdfs --branch-before another_branch 62*3ac0a46fSAndroid Build Coastguard Worker$ testing/tools/safetynet_compare.py path/to/pdfs --branch-before 1a3c5e7 63*3ac0a46fSAndroid Build Coastguard Worker``` 64*3ac0a46fSAndroid Build Coastguard Worker 65*3ac0a46fSAndroid Build Coastguard Worker3. Compare two other branches or commits: 66*3ac0a46fSAndroid Build Coastguard Worker```shell 67*3ac0a46fSAndroid Build Coastguard Worker$ testing/tools/safetynet_compare.py path/to/pdfs --branch-after another_branch --branch-before yet_another_branch 68*3ac0a46fSAndroid Build Coastguard Worker$ testing/tools/safetynet_compare.py path/to/pdfs --branch-after 1a3c5e7 --branch-before 0b2d4f6 69*3ac0a46fSAndroid Build Coastguard Worker$ testing/tools/safetynet_compare.py path/to/pdfs --branch-after another_branch --branch-before 0b2d4f6 70*3ac0a46fSAndroid Build Coastguard Worker``` 71*3ac0a46fSAndroid Build Coastguard Worker 72*3ac0a46fSAndroid Build Coastguard Worker4. Compare two build flag configurations: 73*3ac0a46fSAndroid Build Coastguard Worker```shell 74*3ac0a46fSAndroid Build Coastguard Worker$ gn args out/BuildConfig1 75*3ac0a46fSAndroid Build Coastguard Worker$ gn args out/BuildConfig2 76*3ac0a46fSAndroid Build Coastguard Worker$ testing/tools/safetynet_compare.py path/to/pdfs --build-dir out/BuildConfig2 --build-dir-before out/BuildConfig1 77*3ac0a46fSAndroid Build Coastguard Worker``` 78*3ac0a46fSAndroid Build Coastguard Worker 79*3ac0a46fSAndroid Build Coastguard Workersafetynet_compare.py takes care of checking out the appropriate branch, building 80*3ac0a46fSAndroid Build Coastguard Workerit, running the test cases and comparing results. 81*3ac0a46fSAndroid Build Coastguard Worker 82*3ac0a46fSAndroid Build Coastguard Worker### Profilers 83*3ac0a46fSAndroid Build Coastguard Worker 84*3ac0a46fSAndroid Build Coastguard Workersafetynet_compare.py uses callgrind as a profiler by default. Use --profiler 85*3ac0a46fSAndroid Build Coastguard Workerto specify another one. The supported ones are: 86*3ac0a46fSAndroid Build Coastguard Worker 87*3ac0a46fSAndroid Build Coastguard Worker#### perfstat 88*3ac0a46fSAndroid Build Coastguard Worker 89*3ac0a46fSAndroid Build Coastguard WorkerOnly works on Linux. 90*3ac0a46fSAndroid Build Coastguard WorkerMake sure you have perf by typing in the terminal: 91*3ac0a46fSAndroid Build Coastguard Worker```shell 92*3ac0a46fSAndroid Build Coastguard Worker$ perf 93*3ac0a46fSAndroid Build Coastguard Worker``` 94*3ac0a46fSAndroid Build Coastguard Worker 95*3ac0a46fSAndroid Build Coastguard WorkerThis is a fast profiler, but uses sampling so it's slightly inaccurate. 96*3ac0a46fSAndroid Build Coastguard WorkerExpect variations of up to 1%, which is below the cutoff to consider a 97*3ac0a46fSAndroid Build Coastguard Workerchange significant. 98*3ac0a46fSAndroid Build Coastguard Worker 99*3ac0a46fSAndroid Build Coastguard WorkerUse this when running over large test sets to get good enough results. 100*3ac0a46fSAndroid Build Coastguard Worker 101*3ac0a46fSAndroid Build Coastguard Worker#### callgrind 102*3ac0a46fSAndroid Build Coastguard Worker 103*3ac0a46fSAndroid Build Coastguard WorkerOnly works on Linux. 104*3ac0a46fSAndroid Build Coastguard WorkerMake sure valgrind is installed: 105*3ac0a46fSAndroid Build Coastguard Worker```shell 106*3ac0a46fSAndroid Build Coastguard Worker$ valgrind 107*3ac0a46fSAndroid Build Coastguard Worker``` 108*3ac0a46fSAndroid Build Coastguard Worker 109*3ac0a46fSAndroid Build Coastguard WorkerThis is a slow and accurate profiler. Expect variations of around 100 110*3ac0a46fSAndroid Build Coastguard Workerinstructions. However, this takes about 50 times longer to run than perf stat. 111*3ac0a46fSAndroid Build Coastguard Worker 112*3ac0a46fSAndroid Build Coastguard WorkerUse this when looking for small variations (< 1%). 113*3ac0a46fSAndroid Build Coastguard Worker 114*3ac0a46fSAndroid Build Coastguard WorkerOne advantage is that callgrind can generate `callgrind.out` files (by passing 115*3ac0a46fSAndroid Build Coastguard Worker--output-dir to safetynet_compare.py), which contain profiling information that 116*3ac0a46fSAndroid Build Coastguard Workercan be analyzed to find the cause of a regression. KCachegrind is a good 117*3ac0a46fSAndroid Build Coastguard Workervisualizer for these files. 118*3ac0a46fSAndroid Build Coastguard Worker 119*3ac0a46fSAndroid Build Coastguard Worker#### none 120*3ac0a46fSAndroid Build Coastguard Worker 121*3ac0a46fSAndroid Build Coastguard WorkerRun without any profiler, giving a performance score of 1 always. useful for 122*3ac0a46fSAndroid Build Coastguard Workerrunning image comparisons or debugging the script. 123*3ac0a46fSAndroid Build Coastguard Worker 124*3ac0a46fSAndroid Build Coastguard Worker### Common Options 125*3ac0a46fSAndroid Build Coastguard Worker 126*3ac0a46fSAndroid Build Coastguard WorkerArguments commonly passed to safetynet_compare.py. 127*3ac0a46fSAndroid Build Coastguard Worker 128*3ac0a46fSAndroid Build Coastguard Worker* --profiler: described above. 129*3ac0a46fSAndroid Build Coastguard Worker* --build-dir: this specified the build config with a relative path from the 130*3ac0a46fSAndroid Build Coastguard Workerpdfium src directory to the build directory. Defaults to out/Release. 131*3ac0a46fSAndroid Build Coastguard Worker* --output-dir: where to place the profiling output files. These are 132*3ac0a46fSAndroid Build Coastguard Workercallgrind.out.[test_case] files for callgrind, perfstat does not produce them. 133*3ac0a46fSAndroid Build Coastguard WorkerBy default they are not written. 134*3ac0a46fSAndroid Build Coastguard Worker* --case-order: sort test case results according to this metric. Can be "after", 135*3ac0a46fSAndroid Build Coastguard Worker"before", "ratio" and "rating". If not specified, sort by path. 136*3ac0a46fSAndroid Build Coastguard Worker* --this-repo: use the repository where the script is instead of checking out a 137*3ac0a46fSAndroid Build Coastguard Workertemporary one. This is faster and does not require downloads. Although it 138*3ac0a46fSAndroid Build Coastguard Workerrestores the state of the local repo, if the script is killed or crashes the 139*3ac0a46fSAndroid Build Coastguard Workeruncommitted changes can remain stashed and you may be on another branch. 140*3ac0a46fSAndroid Build Coastguard Worker 141*3ac0a46fSAndroid Build Coastguard Worker### Other Options 142*3ac0a46fSAndroid Build Coastguard Worker 143*3ac0a46fSAndroid Build Coastguard WorkerMost of the time these don't need to be used. 144*3ac0a46fSAndroid Build Coastguard Worker 145*3ac0a46fSAndroid Build Coastguard Worker* --build-dir-before: if comparing different build dirs (say, to test what a 146*3ac0a46fSAndroid Build Coastguard Workerflag flip does), specify the build dir for the “before” branch here and the 147*3ac0a46fSAndroid Build Coastguard Workerbuild dir for the “after” branch with --build-dir. 148*3ac0a46fSAndroid Build Coastguard Worker* --interesting-section: only the interesting section should be measured instead 149*3ac0a46fSAndroid Build Coastguard Workerof all the execution of the test harness. This only works in debug, since in 150*3ac0a46fSAndroid Build Coastguard Workerrelease the delimiters are stripped out. This does not work to compare branches 151*3ac0a46fSAndroid Build Coastguard Workerthat don’t have the callgrind delimiters, as it would otherwise be unfair to 152*3ac0a46fSAndroid Build Coastguard Workercompare a whole run vs the interesting section of another run. 153*3ac0a46fSAndroid Build Coastguard Worker* --machine-readable: output a json with the results that is easier to read by 154*3ac0a46fSAndroid Build Coastguard Workercode. 155*3ac0a46fSAndroid Build Coastguard Worker* --num-workers: how many workers to use to parallelize test case runs. Defaults 156*3ac0a46fSAndroid Build Coastguard Workerto # of CPUs in the machine. 157*3ac0a46fSAndroid Build Coastguard Worker* --threshold-significant: highlight differences that exceed this value. 158*3ac0a46fSAndroid Build Coastguard WorkerDefaults to 0.02. 159*3ac0a46fSAndroid Build Coastguard Worker* --tmp-dir: directory in which temporary repos will be cloned and downloads 160*3ac0a46fSAndroid Build Coastguard Workerwill be cached, if --this-repo is not enabled. Defaults to /tmp. 161*3ac0a46fSAndroid Build Coastguard Worker 162*3ac0a46fSAndroid Build Coastguard Worker## Setup a nightly job 163*3ac0a46fSAndroid Build Coastguard Worker 164*3ac0a46fSAndroid Build Coastguard WorkerCreate a separate checkout of pdfium in a new directory, for example `~/job`. 165*3ac0a46fSAndroid Build Coastguard WorkerThe safetynet_job.py script will run from this directory. This checkout needs to 166*3ac0a46fSAndroid Build Coastguard Workerbe `git pull`'ed when there are changes to the SafetyNet scripts, but otherwise 167*3ac0a46fSAndroid Build Coastguard Workerit can be left alone. 168*3ac0a46fSAndroid Build Coastguard Worker 169*3ac0a46fSAndroid Build Coastguard WorkerCreate a directory to contain the job results, for example `~/job_results`. In 170*3ac0a46fSAndroid Build Coastguard Workereach run, a `.log` file with the results will be written to this directory and a 171*3ac0a46fSAndroid Build Coastguard Workersubdirectory will be created with the other artifacts. 172*3ac0a46fSAndroid Build Coastguard Worker 173*3ac0a46fSAndroid Build Coastguard WorkerSetup a cron job to run safetynet_job.py nightly. The example below runs it at 174*3ac0a46fSAndroid Build Coastguard Worker1:42 AM, over the corpus in two directories: `~/pdf_samples/thousand_pdfs` and 175*3ac0a46fSAndroid Build Coastguard Worker`~/pdf_samples/i18n` 176*3ac0a46fSAndroid Build Coastguard Worker 177*3ac0a46fSAndroid Build Coastguard Worker```shell 178*3ac0a46fSAndroid Build Coastguard Worker@ crontab -e 179*3ac0a46fSAndroid Build Coastguard Worker42 1 * * * bash -lc '~/job/pdfium/testing/tools/safetynet_job.py ~/job_results ~/pdf_samples/thousand_pdfs ~/pdf_samples/i18n --output-to-log >> ~/job_results/cron_nightly.log 2>&1' 180*3ac0a46fSAndroid Build Coastguard Worker``` 181*3ac0a46fSAndroid Build Coastguard Worker 182*3ac0a46fSAndroid Build Coastguard WorkerThe first time the job runs, it will just create a checkpoint as 183*3ac0a46fSAndroid Build Coastguard Worker`~/job_results/last_revision_covered`. From then on, since a checkpoint is 184*3ac0a46fSAndroid Build Coastguard Workeravailable, each run will compare performance with the last checkpoint and update 185*3ac0a46fSAndroid Build Coastguard Workerthe checkpoint. 186*3ac0a46fSAndroid Build Coastguard Worker 187*3ac0a46fSAndroid Build Coastguard Worker## Run image comparison 188*3ac0a46fSAndroid Build Coastguard Worker 189*3ac0a46fSAndroid Build Coastguard WorkerPass the `--png-dir` option pointing at an output directory to compare the output 190*3ac0a46fSAndroid Build Coastguard Workerimages from rendering the "before" and the "after" branches with pdfium_test. 191*3ac0a46fSAndroid Build Coastguard Worker 192*3ac0a46fSAndroid Build Coastguard Worker```shell 193*3ac0a46fSAndroid Build Coastguard Worker$ mkdir ~/output_images 194*3ac0a46fSAndroid Build Coastguard Worker$ testing/tools/safetynet_compare.py ~/pdf_samples --branch-before before_visual_changes --branch-after after_visual_changes --png-dir ~/output_images 195*3ac0a46fSAndroid Build Coastguard Worker``` 196*3ac0a46fSAndroid Build Coastguard Worker 197*3ac0a46fSAndroid Build Coastguard WorkerThis will output and automatically open a `~/output_images/compare.html` file 198*3ac0a46fSAndroid Build Coastguard Workershowing the before/after and the diff. Hover the mouse cursor over the 199*3ac0a46fSAndroid Build Coastguard Workerbefore/after image on the left for an easier visual comparison. The "before" 200*3ac0a46fSAndroid Build Coastguard Workerimage is displayed until the cursor hovers over the image, which is then 201*3ac0a46fSAndroid Build Coastguard Workerreplaced with the "after" image. 202*3ac0a46fSAndroid Build Coastguard Worker 203*3ac0a46fSAndroid Build Coastguard WorkerIt is recommended to use `--profiler=none` with this option. 204