문제

Could someone please explain why the script, below with the setTimeout command, takes a lot longer to execute (400-500 ms) in Greasemonkey than it does in the Firefox console, where it's exactly 100 ms?

var start = new Date ().getTime ();
console.log (
    new Date ().getHours ()+" : " + new Date ().getMinutes () 
    + " : " + new Date ().getSeconds () + " : " 
    + new Date ().getMilliseconds () 
); 

setTimeout (foo,100);
//foo ();

function foo () {
    var time = new Date () - start;
    console.log ("Execution time: " + time + "ms");
}

It's strange, because if I switch setTimeout(foo,100) for a pure foo(), both Greasemonkey and Firefox console execute it lightning fast (~10 ms).

도움이 되었습니까?

해결책

Actually, on my scratch system (Win XP, FF 28.0, GM 1.15) this has very little to do with Greasemonkey and everything to do with (A) the console, and (B) what else Firefox and/or your machine is doing.

JavaScript timers are notoriously bad. (See, also, "Accuracy of JavaScript Time" by John Resig.)

Your results are based off a very small sample size and are not enough data to begin to see an accurate picture. Also browsers, and Greasemonkey, have been changing in regards to this problem, so your versions may differ.

If we use an interval, EG:

setInterval (foo, 100);

function foo () {
    var time = new Date () - start;
    console.log ("Execution time: " + time + "ms");
}

then we can start collecting some statistical data and see what might be going on.

Refining that code and adding statistics gives a userscript like this:

// ==UserScript==
// @name        _Greasemonkey timing test
// @include     https://stackoverflow.com/questions/22738493/*
// @grant       none
// ==/UserScript==
// @grant       GM_addStyle

/*--- Test:
        Both grant modes
        FF console
        Firebug console
        Embedded in page.
*/
var numMeasurements = 100;
var measurementList = [];
var startDate       = new Date ();
var startTime       = startDate.getTime ();

console.log (
    startDate.getHours ()+ " : " + startDate.getMinutes () + " : "
    + startDate.getSeconds () + " : " + startDate.getMilliseconds ()
);

var startDate   = new Date ();  //-- Record time just before interval start.
//setTimeout (timelog, 100);
/*--- WARNING: for delays less than about 50, system "granularity" and
    overhead effects will distort the results even more.
*/
var logTimer    = setInterval (timelog, 100);

function timelog () {
    timelog.numloops    = timelog.numloops  ||  0;

    if (timelog.numloops >= numMeasurements) {
        console.log ('===> Reached ' + timelog.numloops + ' loops.');
        clearInterval (logTimer);

        //--- Calculate stats:
        var stats           = {};
        stats.min           = Number.MAX_VALUE; //-- Always start at opposite
        stats.max           = Number.MIN_VALUE;
        stats.sum           = 0;
        stats.mean          = 0;
        stats.sumSqrs       = 0;
        stats.stdDev        = 0;
        stats.N             = measurementList.length;

        for (var J = 0;  J < stats.N;  ++J) {
            var measVal     = measurementList[J];
            stats.sum      += measVal;
            stats.sumSqrs  += measVal * measVal;

            if (measVal > stats.max)    stats.max = measVal;
            if (measVal < stats.min)    stats.min = measVal;
        }

        stats.mean          = stats.sum / stats.N;
        stats.stdDev        = Math.sqrt (
            (stats.sumSqrs / stats.N) - (stats.mean * stats.mean)
        );


        //--- Display stats:
        var decsToDisplay   = 1;
        console.log (' Measurements: ' + stats.N);
        console.log ('      Average: ' + stats.mean.toFixed (decsToDisplay) );
        console.log ('   Min to Max: ' + stats.min + ' to ' + stats.max);
        console.log ('Std Deviation: ' + stats.stdDev.toFixed (decsToDisplay) );
    }
    else {
        timelog.numloops++;
        var timeNow = new Date ();
        var timeDif = timeNow - startDate;
        measurementList.push (timeDif);
        console.log (
            '==> Execution time ('
              //-- Left-pad value for more legible column, 3 chars wide.
            + ("  " + timelog.numloops).slice (-3) + '): '
              //-- Left-pad value for more legible column, 4 chars wide.
            + ("   " + timeDif).slice (-4) + ' ms   '
            , timeNow.getTime ()
        );
        startDate   = timeNow;
    }
}


Install the script and/or you can see this code in action at jsFiddle.

To see if Greasemonkey is a factor, we should test at least these scenarios:

  1. Code in the Firefox console.
  2. Code in a webpage both to the console and with the consoles closed.
  3. Code in a Greasemonkey script with sandbox active (@grant GM_addStyle set).
  4. Code in a Greasemonkey script with @grant none active.
  5. Code in the Firebug console.

Ideally, the web page and system circumstances should remain as constant as possible.

Testing with a 100 ms delay and a 100 samples (probably the minimum values for meaningful data), I get (all values in milliseconds):

//-- These first were run against stackoverflow.com/q/22738493

                                                  Std
Condition                      Min  Max   Avg   Deviation
--------------------------     ---  ---  -----  ---------
Firefox console, run 1:          0  518  138.9    133.2
Firefox console, run 2:          1  466  215.4    209.6

Firebug console, run 1:          1  144  100.5     21.8
Firebug console, run 2:          3  209  100.9     25.2

GM to FF cons, in sandbox:       0  398  135.4    112.9
GM to FF cons, @grant none 1:    0  387  125.3     97.4
GM to FF cons, @grant none 2:    0  563  145.2    122.0
GM to Firebug console:          38  401  109.4     49.1

//-- These were run against jsfiddle.net/caL94/2

jsFiddle to FF console 1:        2  375  113.3     82.5
jsFiddle to FF console 2:        1  575  169.7    171.1
jsFiddle to Firebug console:    27  219  103.5     24.9

jsFiddle, consoles closed 1:     0  530  105.3     57.2
jsFiddle, consoles closed 2:     5  195  100.0     21.9


From these numbers, it should be clear that:

  1. JavaScript timers are no good for precision timing. (But they probably are adequate for most practical web-page uses.)
  2. Timing code in Greasemonkey performs just as well as code running in the Firebug console or not logging to any console.
  3. The real big hit is logging to Firefox's console (CtrlShiftK) -- which radically decreases timer accuracy.
  4. Computer and browser (and even webpage) state all impact accuracy, and repeatability, to a large degree.
  5. Also, remember that browsers throttle javascript timers if you switch away from the tab (loses focus).
라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top