new Date() is slow on Windows

RESOLVED FIXED in mozilla32

Status

()

Core
JavaScript Engine
RESOLVED FIXED
7 years ago
3 years ago

People

(Reporter: m_kato, Assigned: jandem)

Tracking

(Blocks: 1 bug, {perf})

Trunk
mozilla32
All
Windows Vista
Points:
---
Dependency tree / graph

Firefox Tracking Flags

(blocking2.0 -)

Details

(URL)

Attachments

(1 attachment)

(Reporter)

Description

7 years ago
Original is http://jsbenchmark.celtickane.com/Run.aspx

STEP
====
Run the following

var t = new Date(); for (var i = 0; i < 1000000; i++) { var a = new Date(); } print(new Date() - t );


RESULT
======
This bench result is 9 times slower than V8 on Windows.  Also, if I use Mac or Linux, this bench result is same as V8.

PRMJ_Now() for Windows becomes complex code to fix Bug 363258, so new Date() is slow.  We should rewrite PRMJ_Now() to improve new Date() performance.
Blocks: 363258
No longer depends on: 363258

Comment 1

7 years ago
I can confirm this, running the previous script as a bookmarklet:
javascript:var t = new Date(); for (var i = 0; i < 1000000; i++) { var a = new Date(); }alert(new Date() - t );

I got the following scores:
Latest Minefield: 2165
Latest Chrome Dev Build: 297

This wouldn't be a problem if it weren't for the fact that plenty of js benchmarks on the net create a new date every iteration of their test over sometimes thousands of iterations.

Firefox ends much looking much slower than it actually is and may even be hiding the fact that it's faster on many tests.
What OS is comment 1 and comment 0 on, if I might ask?  Date creation is particularly slow on XP, but I thought on recent Windows it was decent....
(In reply to comment #2)
> What OS is comment 1 and comment 0 on, if I might ask?  Date creation is
> particularly slow on XP, but I thought on recent Windows it was decent....

We can probably make it decent. Vista was barely out when I wrote that code and hardware/software has improved so that we can frequently avoid some of the overhead. Might be worth looking at Chrome's implementation again (they reference ours).

Comment 4

7 years ago
(In reply to comment #2)
> What OS is comment 1 and comment 0 on, if I might ask?  Date creation is
> particularly slow on XP, but I thought on recent Windows it was decent....

On win7 32-bit I get these numbers:

Latest trunk: 1239
Latest chrome dev: 173

Comment 5

7 years ago
Created attachment 484947 [details]
Testcase showing slowness

I've taken the Factorials benchmark from jsBenchmark and modified it to show how much this issue can affect the measured speed compared to other browsers.

The benchmark originally just calculates how many iterations it can do in 100ms and then return how many iterations performed per ms. I modified it just return the iterations count. I created a duplicate function that performs the same test but for a variable amount of iterations passed to it as a parameter and it returns the amount of ms it took to perform that many iterations. The first function creates a Date object every iteration and the second one doesn't.

In this testcase, i run the original function and pass its return value, which would be the amount of iterations it performed in 100ms to the modified function, and it returns how many ms it took to perform that many iterations.

I get the following results:
Minefield: 48ms~
Chrome: 86ms~

That's a huge difference! This means up to 50% of our time in this particular benchmark goes into just measuring elapsed time! This affects almost all small benchmarks with huge amounts iteration and there are many. This is bad.

bz: I'm on Windows 7 32bit
Yeah, that doesn't sound good.  On Mac I'm seeing numbers like 95ms for that last testcase....

And e.g. dromaeo does call Date() on every single loop iteration.  So yeah, solving this sounds like it could noticeable improve our Windows dromaeo scores.  Another case where doing all the profiling on Mac is not helping us.
blocking2.0: --- → ?
(Reporter)

Comment 7

7 years ago
(In reply to comment #3)

> We can probably make it decent. Vista was barely out when I wrote that code and
> hardware/software has improved so that we can frequently avoid some of the
> overhead. Might be worth looking at Chrome's implementation again (they
> reference ours).

V8 implementation is cached SystemTime (updated per 1min) + multimedia timer (resolution is 1ms).
QueryPerformanceCounter() with cached QueryPerformanceFrequency() is extremely fast on Windows but would require some processing - not sure if this information is useful or not, but on Windows XP I've seen a counter frequency as high as the system clock, while in Windows 7 at least it appears to be 10x lower (e.g. on a 3.2GHz processor the counter frequency might be as high as 3.2GHz in Windows XP, but is more likely 3.2MHz on Windows 7).
(Reporter)

Comment 9

7 years ago
(In reply to comment #8)
> QueryPerformanceCounter() with cached QueryPerformanceFrequency() is extremely
> fast on Windows but would require some processing - not sure if this
> information is useful or not, but on Windows XP I've seen a counter frequency
> as high as the system clock, while in Windows 7 at least it appears to be 10x
> lower (e.g. on a 3.2GHz processor the counter frequency might be as high as
> 3.2GHz in Windows XP, but is more likely 3.2MHz on Windows 7).

QueryPerformanceCount() is *slow*.  Fastest is multimedia timer (but it causes more interrupt for CPU.  So it spends battery on Laptop PC.).  RDTSC is also fastest, but this is bad.  This value depends on frequency.
I suppose it depends on your application ... Reading up on multimedia timers, I guess they update at most once a millisecond and do it in their own thread, so if you just want to know if a millisecond or more passed since the last time you called Date(), using a multimedia timer means simply checking its value.

QueryPerformanceCount() is useful if you want to know exactly how much time passed - you could probably implement it more-or-less the same way timers work by giving it its own thread and using Sleep() in between calls. But there's no need to reinvent the wheel.

When I wrote my last post I was thinking in terms of SystemTime, in which case QueryPerformanceCount() should just be a serialized RDTSC ... if you're saying the call actually takes a long time to return, I haven't seen that.

Comment 11

7 years ago
This should probably block bug 499198 (peacekeeper) since peacekeeper also calculates "new Date()" for every iteration.  If the iteration counts are hardcoded in peacekeeper, Firefox does a lot better and even beats out Chrome in two of them.
The worst part is that peacekeeper ends up not using the date for anything for most of those tests, because we hit its iteration cap first....

Updated

7 years ago
blocking2.0: ? → -

Comment 13

7 years ago
blocking2.0: -

Does this mean that this bug wont be fixed for Firefox 4? Not even Wanted?

Updated

7 years ago
Blocks: 499198

Comment 14

6 years ago
If Chrome is using a multimedia timer, then what does it do when it's running a laptop that's running on a battery? Does it decrease the resolution?
Last I checked, no.  roc, that's what your testing showed, right?
Yes.
Is new Date() still slow on Windows? This bug was filed almost three years ago. :)
Keywords: perf

Comment 18

3 years ago
I'm on Windows 7 and I already tested a few bugs (tests from Peacekeeper) where Nightly is a lot slower on Windows than on Mac. Hope somebody finds the problem!

Using bookmarklet from comment 1:
Nightly 29.0a1 (2014-01-18) - 1395
Chrome 32 - 1950
IE 11 - 880

Using the testcase from comment 5:
Nightly 29.0a1 (2014-01-18) - 51ms
Chrome 32 - 63ms
IE 11 - 66ms

From what I understand, they don't agree about the results...
Yep.  Microbenchmarking is hard.  :(
Hannes, you've been working on Peacekeeper improvements, right? :-)
(In reply to Robert O'Callahan (:roc) (Mozilla Corporation) from comment #20)
> Hannes, you've been working on Peacekeeper improvements, right? :-)

Thanks for cc'ing. This bug wasn't on my radar.
Some quick digging shows that PRMJ_Now is called and it's the only platform specific part of the code. Retrieving the date involves locking two mutexes (!) in thread safe builds on WIN_XP. See also http://mxr.mozilla.org/mozilla-central/source/js/src/prmjtime.cpp which mentions bug 363258 as a cause for the code to be that slow and complex. Might be worth to see what V8 does on WIN platforms.
See here: https://code.google.com/p/v8/source/browse/trunk/src/platform/time.cc#378.
Sorry, Time::Now and not TimeTickes::Now is used for Date.now: https://code.google.com/p/v8/source/browse/trunk/src/platform/time.cc#164.
(Assignee)

Comment 25

3 years ago
This is still hurting benchmark perf on Windows, see bug 916851 for instance. Taking.
Assignee: general → jdemooij
Status: NEW → ASSIGNED
(Assignee)

Comment 26

3 years ago
I think I'm just going to import V8's timer code and rm prmjtime.cpp/h If anybody has a better idea please let me know.

V8's timer is also process-wide and they use a mutex, at least on Windows. I wonder if we could store this in TLS so that we don't need the locking. The lock probably doesn't really matter though... Thoughts?
I don't really know the issue here, but could the high resolution platform timers be used here? The Windows side of that was implemented in bug 676349, and I guess the main header would be http://dxr.mozilla.org/mozilla-central/source/xpcom/ds/TimeStamp.h (sorry if this is just noise, but this stuff didn't exist 3 years ago).
TimeStamp is a monotonic time.  It doesn't have the same behavior as JS Date, which is wall-clock time...  That's ignoring the fact that the JS engine can't depend on XPCOM...
Ah okay, I guess that's no use then (barring potential code reuse, but it sounds like jandem already has a plan).
(Assignee)

Comment 30

3 years ago
I ported the V8 implementation. Problem is that it uses timeGetTime() on Windows and this has a bad precision (~15ms by default). This can be improved with timeBeginPeriod, but this is bad for battery life. See the following blog post:

http://www.belshe.com/2010/06/04/chrome-cranking-up-the-clock/

And this comment in the Chromium source code, src/base/time/time_win.cc:

// QueryPerformanceCounter is the logical choice for a high-precision timer.
// However, it is known to be buggy on some hardware.  Specifically, it can
// sometimes "jump".  On laptops, QPC can also be very expensive to call.
// It's 3-4x slower than timeGetTime() on desktops, but can be 10x slower
// on laptops.  A unittest exists which will show the relative cost of various
// timers on any system.
//
// The next logical choice is timeGetTime().  timeGetTime has a precision of
// 1ms, but only if you call APIs (timeBeginPeriod()) which affect all other
// applications on the system.  By default, precision is only 15.5ms.
// Unfortunately, we don't want to call timeBeginPeriod because we don't
// want to affect other applications.  Further, on mobile platforms, use of
// faster multimedia timers can hurt battery life.  See the intel
// article about this here:
// http://softwarecommunity.intel.com/articles/eng/1086.htm
//
// To work around all this, we're going to generally use timeGetTime().  We
// will only increase the system-wide timer if we're not running on battery
// power.  Using timeBeginPeriod(1) is a requirement in order to make our
// message loop waits have the same resolution that our time measurements
// do.  Otherwise, WaitForSingleObject(..., 1) will no less than 15ms when
// there is nothing else to waken the Wait.

So getting high precision timers on Windows is really complicated, especially if you don't want to hurt battery life :(
(Assignee)

Comment 31

3 years ago
Apparently Windows 8 has a new GetSystemTimePreciseAsFileTime function:

http://msdn.microsoft.com/en-us/library/windows/desktop/hh706895%28v=vs.85%29.aspx

It has "the highest possible level of precision (<1us)", so that's promising...

I read somewhere that it's slower than GetSystemTimeAsFileTime, but it may still be faster than what we're doing now, and at least much simpler. I'll do some measurements tomorrow.

And of course many (most?) users are still on Windows 7, but improving the situation on Windows 8+ is better than nothing.
(Assignee)

Comment 32

3 years ago
For the following micro-benchmark:

function f() {
    var t = new Date;
    var res = 0;
    for (var i=0; i<10000000; i++)
        res = Date.now();
    alert(new Date - t);
    this.x = res;
}

I got the following numbers on a (pretty average) Windows 8 laptop for GetSystemTimePreciseAsFileTime:

* before: 6050 ms
* after : 1460 ms

Precision also looks good. So on Windows 8 we can make this much faster. Maybe we could also optimize PRMJ_Now a bit for older Windows versions, will look into that tomorrow.
(Assignee)

Updated

3 years ago
Depends on: 1004923
By the way, here's a short article on the difference between GetTickCount() (which the TimeStamp code uses) and timeGetTime() (which the V8 code uses): http://blogs.msdn.com/b/larryosterman/archive/2009/09/02/what-s-the-difference-between-gettickcount-and-timegettime.aspx

It seems to me that while timeGetTime() may update in more regular amounts, GetTickCount() will more accurately represent when the last tick happened, so it would be better for correlating the result with QueryPerformanceCounter() (i.e. ideally you want to synchronize with updates to the result of GetTickCount(), then update the result between ticks using QPC()).
(Assignee)

Comment 34

3 years ago
I think I figured out why our implementation is so slow: it calls GetSystemTimeAdjustment (and uses it incorrectly even) and at least on Windows 7 that makes us almost 4x slower.

See bug 1004923 comment 16 for more info.
(Assignee)

Comment 35

3 years ago
I initially filed bug 1004923 to improve this on Windows 8, but it also made this about 4x faster on older Windows versions by removing a super slow API call. On a simple Date.now() micro-benchmark I get:

Before: 2805 ms
After:   751 ms
Chrome: 1150 ms
Status: ASSIGNED → RESOLVED
Last Resolved: 3 years ago
Resolution: --- → FIXED
Target Milestone: --- → mozilla32
You need to log in before you can comment on or make changes to this bug.