⚠ Archived content — this site is no longer maintained.   Current WebKit documentation is at docs.webkit.org.

Changeset 185425 in webkit


Ignore:
Timestamp:
Jun 10, 2015, 11:44:50 AM (11 years ago)
Author:
fpizlo@apple.com
Message:

JetStream should have a more rational story for jitter-oriented latency tests
​https://bugs.webkit.org/show_bug.cgi?id=145762

Reviewed by Geoffrey Garen.

JetStream has some latency tests that are meant to measure jitter. Prior to this change, they
did this by computing the RMS. But the RMS is a pretty bad metric. The thing that it rewards
isn't really the thing that you'd want your browser to do. These RMS-based tests involve taking
the geomean of the RMS of some samples and the sample average. The lower the geomean, the better
(in the JetStream harness we then invert the scores so that higher is better, but let's ignore
that for this discussion and assume that lower is better). Here's an example of how this can go
bad. A browser that always computes a task in some horribly long time (say, 1000ms) but never
varies that time will perform better than a browser that usually computes the task super quickly
(say, 10ms) and sometimes just a little bit less quickly (say, 15ms). The former browser will
have an RMS of 0 and an average of 1000. The latter will have a RMS somewhere around 3.5 and an
average of 12.5 (assuming equal probability of 10ms and 15ms). The geomean of (0, 1000) is 0.
The geomean of (3.5, 12.5) is 6.6. Lower is better, so the former browser scores higher - even
though it's obviously never better to have a browser always complete a task in 1000ms when a
different browser can do it in 15ms in the worst case.

JetStream should not have this pathology. The right way of avoiding it is to replace RMS with
some other metric of how bad things get. A good metric is the average of the worst percentile.
The worst 1% or the worst 5% would be good things to average. This will catch cases where the VM
jittered due to JIT or GC, but it never have the pathology that we end up giving the better score
to a VM whose best case is worst than another VM's worst case.

For now, this change uses the highest samples above the 95% percentile. I'm not yet sure if that
is the best thing - it might include too many scores that are around the best-case performance -
but it's certainly better than RMS and it might be good enough to keep. But because of that
uncertainty, I'm setting the version to be "1.1-alpha1" to indicate that we aren't ready to
release this yet.

  • JetStream/Octane2/base.js:

(.this.Setup.setup.setup):
(.this.TearDown.tearDown.tearDown):
(BenchmarkSuite.GeometricMeanTime):
(BenchmarkSuite.AverageAbovePercentile):
(BenchmarkSuite.GeometricMeanLatency):
(BenchmarkSuite.prototype.NotifyStep):
(BenchmarkSuite.prototype.RunSingleBenchmark):

  • JetStream/Octane2/mandreel.js:

(setupMandreel):
(updateMandreelStats):
(startMandreelTimer):
(latencyMandreel):
(tearDownMandreel):
(RMSMandreel): Deleted.

  • JetStream/Octane2/splay.js:

(GenerateKey):
(SplayUpdateStats):
(InsertNewNode):
(SplayTearDown):
(SplayRMS): Deleted.

  • JetStream/create.rb:
Location:
trunk
Files:
12 edited

Legend:

Unmodified
Added
Removed
  • trunk/PerformanceTests/ChangeLog

    r185162 r185425  
     12015-06-08  Filip Pizlo  <fpizlo@apple.com>
     2
     3        JetStream should have a more rational story for jitter-oriented latency tests
     4        https://bugs.webkit.org/show_bug.cgi?id=145762
     5
     6        Reviewed by Geoffrey Garen.
     7       
     8        JetStream has some latency tests that are meant to measure jitter.  Prior to this change, they
     9        did this by computing the RMS.  But the RMS is a pretty bad metric.  The thing that it rewards
     10        isn't really the thing that you'd want your browser to do.  These RMS-based tests involve taking
     11        the geomean of the RMS of some samples and the sample average.  The lower the geomean, the better
     12        (in the JetStream harness we then invert the scores so that higher is better, but let's ignore
     13        that for this discussion and assume that lower is better).  Here's an example of how this can go
     14        bad.  A browser that always computes a task in some horribly long time (say, 1000ms) but never
     15        varies that time will perform better than a browser that usually computes the task super quickly
     16        (say, 10ms) and sometimes just a little bit less quickly (say, 15ms).  The former browser will
     17        have an RMS of 0 and an average of 1000.  The latter will have a RMS somewhere around 3.5 and an
     18        average of 12.5 (assuming equal probability of 10ms and 15ms).  The geomean of (0, 1000) is 0.
     19        The geomean of (3.5, 12.5) is 6.6.  Lower is better, so the former browser scores higher - even
     20        though it's obviously never better to have a browser always complete a task in 1000ms when a
     21        different browser can do it in 15ms in the worst case.
     22
     23        JetStream should not have this pathology.  The right way of avoiding it is to replace RMS with
     24        some other metric of how bad things get.  A good metric is the average of the worst percentile.
     25        The worst 1% or the worst 5% would be good things to average.  This will catch cases where the VM
     26        jittered due to JIT or GC, but it never have the pathology that we end up giving the better score
     27        to a VM whose best case is worst than another VM's worst case.
     28       
     29        For now, this change uses the highest samples above the 95% percentile. I'm not yet sure if that
     30        is the best thing - it might include too many scores that are around the best-case performance -
     31        but it's certainly better than RMS and it might be good enough to keep. But because of that
     32        uncertainty, I'm setting the version to be "1.1-alpha1" to indicate that we aren't ready to
     33        release this yet.
     34
     35        * JetStream/Octane2/base.js:
     36        (.this.Setup.setup.setup):
     37        (.this.TearDown.tearDown.tearDown):
     38        (BenchmarkSuite.GeometricMeanTime):
     39        (BenchmarkSuite.AverageAbovePercentile):
     40        (BenchmarkSuite.GeometricMeanLatency):
     41        (BenchmarkSuite.prototype.NotifyStep):
     42        (BenchmarkSuite.prototype.RunSingleBenchmark):
     43        * JetStream/Octane2/mandreel.js:
     44        (setupMandreel):
     45        (updateMandreelStats):
     46        (startMandreelTimer):
     47        (latencyMandreel):
     48        (tearDownMandreel):
     49        (RMSMandreel): Deleted.
     50        * JetStream/Octane2/splay.js:
     51        (GenerateKey):
     52        (SplayUpdateStats):
     53        (InsertNewNode):
     54        (SplayTearDown):
     55        (SplayRMS): Deleted.
     56        * JetStream/create.rb:
     57
    1582015-06-03  Zalan Bujtas  <zalan@apple.com>
    259
  • trunk/PerformanceTests/JetStream/Octane2/base.js

    r183091 r185425  
    11// Copyright 2013 the V8 project authors. All rights reserved.
     2// Copyright (C) 2015 Apple Inc. All rights reserved.
    23// Redistribution and use in source and binary forms, with or without
    34// modification, are permitted provided that the following conditions are
    … …  
    4748// running the benchmark, but the running time of these functions will
    4849// not be accounted for in the benchmark score.
    49 function Benchmark(name, doWarmup, doDeterministic, run, setup, tearDown, rmsResult, minIterations) {
     50function Benchmark(name, doWarmup, doDeterministic, run, setup, tearDown, latencyResult, minIterations) {
    5051  this.name = name;
    5152  this.doWarmup = doWarmup;
    … …  
    5455  this.Setup = setup ? setup : function() { };
    5556  this.TearDown = tearDown ? tearDown : function() { };
    56   this.rmsResult = rmsResult ? rmsResult : null;
     57  this.latencyResult = latencyResult ? latencyResult : null;
    5758  this.minIterations = minIterations ? minIterations : 32;
    5859}
    … …  
    190191
    191192
    192 // Computes the geometric mean of a set of rms measurements.
     193// Computes the average of the worst samples. For example, if percentile is 99, this will report the
     194// average of the worst 1% of the samples.
     195BenchmarkSuite.AverageAbovePercentile = function(numbers, percentile) {
     196  // Don't change the original array.
     197  numbers = numbers.slice();
     198 
     199  // Sort in ascending order.
     200  numbers.sort(function(a, b) { return a - b; });
     201 
     202  // Now the elements we want are at the end. Keep removing them until the array size shrinks too much.
     203  // Examples assuming percentile = 99:
     204  //
     205  // - numbers.length starts at 100: we will remove just the worst entry and then not remove anymore,
     206  //   since then numbers.length / originalLength = 0.99.
     207  //
     208  // - numbers.length starts at 1000: we will remove the ten worst.
     209  //
     210  // - numbers.length starts at 10: we will remove just the worst.
     211  var numbersWeWant = [];
     212  var originalLength = numbers.length;
     213  while (numbers.length / originalLength > percentile / 100)
     214    numbersWeWant.push(numbers.pop());
     215 
     216  var sum = 0;
     217  for (var i = 0; i < numbersWeWant.length; ++i)
     218    sum += numbersWeWant[i];
     219 
     220  var result = sum / numbersWeWant.length;
     221 
     222  // Do a sanity check.
     223  if (numbers.length && result < numbers[numbers.length - 1]) {
     224    throw "Sanity check fail: the worst case result is " + result +
     225      " but we didn't take into account " + numbers;
     226  }
     227 
     228  return result;
     229}
     230
     231
     232// Computes the geometric mean of a set of latency measurements.
    193233BenchmarkSuite.GeometricMeanLatency = function(measurements) {
    194234  var log = 0;
    … …  
    294334    if (data.runs < benchmark.minIterations) return data;
    295335    var usec = (data.elapsed * 1000) / data.runs;
    296     var rms = (benchmark.rmsResult != null) ? benchmark.rmsResult() : 0;
    297     this.NotifyStep(new BenchmarkResult(benchmark, usec, rms));
     336    var latencySamples = (benchmark.latencyResult != null) ? benchmark.latencyResult() : [0];
     337    var percentile = 95;
     338    var latency = BenchmarkSuite.AverageAbovePercentile(latencySamples, percentile) * 1000;
     339    this.NotifyStep(new BenchmarkResult(benchmark, usec, latency));
    298340    return null;
    299341  }
  • trunk/PerformanceTests/JetStream/Octane2/mandreel.js

    r183091 r185425  
    11// Portions copyright 2012 Google, Inc.
    22// Copyright 2012 Onan Games. All rights reserved.
     3// Copyright (C) 2015 Apple Inc. All rights reserved.
    34// Redistribution and use in source and binary forms, with or without
    45// modification, are permitted provided that the following conditions are
    … …  
    3435                                                           setupMandreel,
    3536                                                           tearDownMandreel,
    36                                                            RMSMandreel,
     37                                                           latencyMandreel,
    3738                                                           4)]);
    3839
    39 var mandreelSumSquaredPauses = 0;
    40 var mandreelSamples = 0;
     40var mandreelPauseTimes = [];
    4141var mandreelSampleTimeStart = 0.0;
    4242
    … …  
    8181  var pause = time - mandreelSampleTimeStart;
    8282  mandreelSampleTimeStart = time;
    83   mandreelSumSquaredPauses += (pause * pause);
    84   mandreelSamples++;
     83  mandreelPauseTimes.push(pause);
    8584}
    8685
    … …  
    8988}
    9089
    91 function RMSMandreel() {
    92   return Math.round(Math.sqrt(mandreelSumSquaredPauses / mandreelSamples) * 100);
     90function latencyMandreel() {
     91  return mandreelPauseTimes;
    9392}
    9493
    … …  
    109108  heapDouble = null;
    110109  mandreelAppUsePackAsyncTexture = null;
    111   mandreelSumSquaredPauses = 0;
    112   mandreelSamples = 0;
     110  mandreelPauseTimes = [];
    113111}
    114112
  • trunk/PerformanceTests/JetStream/Octane2/splay.js

    r183091 r185425  
    11// Copyright 2009 the V8 project authors. All rights reserved.
     2// Copyright (C) 2015 Apple Inc. All rights reserved.
    23// Redistribution and use in source and binary forms, with or without
    34// modification, are permitted provided that the following conditions are
    … …  
    3637var Splay = new BenchmarkSuite('Splay', [81491, 2739514], [
    3738  new Benchmark("Splay", true, false,
    38     SplayRun, SplaySetup, SplayTearDown, SplayRMS)
     39    SplayRun, SplaySetup, SplayTearDown, SplayLatency)
    3940]);
    4041
    … …  
    6970}
    7071
    71 var splaySamples = 0;
    72 var splaySumOfSquaredPauses = 0;
    73 
    74 function SplayRMS() {
    75   return Math.round(Math.sqrt(splaySumOfSquaredPauses / splaySamples) * 10000);
     72var splaySamples = [];
     73
     74function SplayLatency() {
     75  return splaySamples;
    7676}
    7777
    … …  
    7979  var pause = time - splaySampleTimeStart;
    8080  splaySampleTimeStart = time;
    81   splaySamples++;
    82   splaySumOfSquaredPauses += pause * pause;
     81  splaySamples.push(pause);
    8382}
    8483
    … …  
    120119  splayTree = null;
    121120
    122   splaySamples = 0;
    123   splaySumOfSquaredPauses = 0;
     121  splaySamples = [];
    124122
    125123  // Verify that the splay tree has the right size.
  • trunk/PerformanceTests/JetStream/Reference.js

    r183091 r185425  
    5555    "regexp-2010": 61.82352941176467,
    5656    "splay": 0.9286376274328075,
    57     "splay-latency": 32.399999999999984,
     57    "splay-latency": 3.524855736311738,
    5858    "navier-stokes": 9.653846153846146,
    5959    "pdfjs": 88.4166666666666,
    6060    "mandreel": 157.14285714285708,
    61     "mandreel-latency": 0.5129999999999998,
     61    "mandreel-latency": 1.4940079129942474,
    6262    "gbemu": 135.9999999999998,
    6363    "code-first-load": 2.3249465349251905,
  • trunk/PerformanceTests/JetStream/create.rb

    r183091 r185425  
    11#!/usr/bin/env ruby
    22
    3 # Copyright (C) 2014 Apple Inc. All rights reserved.
     3# Copyright (C) 2014, 2015 Apple Inc. All rights reserved.
    44#
    55# Redistribution and use in source and binary forms, with or without
    … …  
    2727require "shellwords"
    2828
    29 VERSION = "1.0.1"
     29VERSION = "1.1-alpha1"
    3030DIRECTORY_NAME = "JetStream-#{VERSION}"
    3131
  • trunk/Source/JavaScriptCore/bytecode/CodeBlock.h

    r185382 r185425  
    538538        return hasExitSite(locker, site);
    539539    }
     540   
     541    size_t numberOfExitSites() const
     542    {
     543        ConcurrentJITLocker locker(m_lock);
     544        return m_exitProfile.size();
     545    }
    540546
    541547    DFG::ExitProfile& exitProfile() { return m_exitProfile; }
  • trunk/Source/JavaScriptCore/bytecode/DFGExitProfile.cpp

    r176533 r185425  
    4545        return true;
    4646    }
    47    
    48     // Don't add it if it's already there. This is O(n), but that's OK, because we
    49     // know that the total number of places where code exits tends to not be large,
    50     // and this code is only used when recompilation is triggered.
    51     for (unsigned i = 0; i < m_frequentExitSites->size(); ++i) {
    52         if (m_frequentExitSites->at(i) == site)
    53             return false;
    54     }
    55    
     47
     48    // Always add even if it's a duplicate. The side-effect of doing this is it gives us a
     49    // complete count of the number of times we've exited here.
    5650    m_frequentExitSites->append(site);
    5751    return true;
  • trunk/Source/JavaScriptCore/bytecode/DFGExitProfile.h

    r176553 r185425  
    180180    }
    181181   
     182    size_t size() const
     183    {
     184        if (!m_frequentExitSites)
     185            return 0;
     186        return m_frequentExitSites->size();
     187    }
     188   
    182189private:
    183190    friend class QueryableExitProfile;
  • trunk/Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp

    r185409 r185425  
    12741274   
    12751275    // It might be possible to inline.
    1276     return codeBlock->instructionCount();
     1276    return codeBlock->instructionCount() * pow(1 + codeBlock->numberOfExitSites(), Options::exitSitePowerForInlineCost());
    12771277}
    12781278
  • trunk/Source/JavaScriptCore/runtime/Options.h

    r184354 r185425  
    205205    v(unsigned, maximumOptimizationCandidateInstructionCount, 100000, nullptr) \
    206206    \
    207     v(unsigned, maximumFunctionForCallInlineCandidateInstructionCount, 180, nullptr) \
    208     v(unsigned, maximumFunctionForClosureCallInlineCandidateInstructionCount, 100, nullptr) \
    209     v(unsigned, maximumFunctionForConstructInlineCandidateInstructionCount, 100, nullptr) \
     207    v(unsigned, maximumFunctionForCallInlineCandidateInstructionCount, 250, nullptr) \
     208    v(unsigned, maximumFunctionForClosureCallInlineCandidateInstructionCount, 180, nullptr) \
     209    v(unsigned, maximumFunctionForConstructInlineCandidateInstructionCount, 180, nullptr) \
    210210    \
    211211    v(unsigned, maximumFTLCandidateInstructionCount, 20000, nullptr) \
     212    \
     213    v(double, exitSitePowerForInlineCost, 0.5, nullptr) \
    212214    \
    213215    /* Depth of inline stack, so 1 = no inlining, 2 = one level, etc. */ \
  • trunk/Tools/Scripts/run-jsc-benchmarks

    r183787 r185425  
    28442844  ARGV.each {
    28452845    | vm |
    2846     if vm =~ /([a-zA-Z0-9_ ]+):/
     2846    if vm =~ /([a-zA-Z0-9_ .]+):/
    28472847      name = $1
    28482848      nameKind = :given
    … …  
    28532853    end
    28542854    envs = []
    2855     while vm =~ /([a-zA-Z0-9_]+)=([a-zA-Z0-9_:]+):/
     2855    while vm =~ /([a-zA-Z0-9_]+)=([a-zA-Z0-9_:.]+):/
    28562856      envs << [$1, $2]
    28572857      vm = $~.post_match
Note: See TracChangeset for help on using the changeset viewer.