An idea how to probably improve Date.now() (new Date, etc.) speed.

449 views
Skip to first unread message

Alexey Kupershtokh

unread,
Oct 25, 2012, 2:48:50 PM10/25/12
to nod...@googlegroups.com
This is a relatively often used function in node.js. I think speeding it up would be great. So I have an idea how to improve it.
What if we could set it every 1 ms to a variable, similar to how --prof works.
This way we could get real time with 50-100 times less impact that it has now.
So how stable --prof is? Is it really executed close to every ms, working ok along with GC and so on?
What do you think about the idea? Any critics?

Ben Noordhuis

unread,
Oct 25, 2012, 9:04:59 PM10/25/12
to nod...@googlegroups.com
I don't really care about speeding up Date.now() because it's already
near optimal from a performance perspective. On Linux systems, it
doesn't even require a system call, it simply reads the timestamp from
the VDSO.

Weeding out new Date() call sites is generally a good thing: the Date
constructor is about 3x as slow as Date.now() and uses more memory to
boot. I don't expect to see user-visible improvements outside of
micro-benchmarks, though.

Re: --prof, I don't quite see how it's connected to all this but yes,
the profiler runs once every millisecond. It's a separate thread that
wakes up at fixed intervals and records the main thread's instruction
pointer (and some other bits).

Actually, the profiler always runs, regardless of whether or not you
specified --prof. It's what V8 uses to find out what code is hot.

Alexey Kupershtokh

unread,
Oct 25, 2012, 11:31:08 PM10/25/12
to nod...@googlegroups.com
Currently after removing one excessive call to Date.now makes the other Date.now call require as much time as all the other logic in setTimeout() + active() + all the _linklist stuff (L.isempty, L.remove, L.append, etc.). I think it can be better.

Looking from another side, Date.now() can be calculated about 1.4M ops/sec rate on my pc. But EventEmitter can perform at 5-20M ops/sec in simple emit scenarios.

Ok, let's take into account an http benchmark, you were referring https://github.com/joyent/node/issues/4191 . How many Date.now() are performed for each request? Let's say this number is 1. Let's calculate Date.now cpu usage: 21K/1.4M is 1.5% spent only on time calculation. Is it that much to optimize? I don't actually know.

So I would say, that Date.now() is fast enough but not optimal.

Regarding the --perf, let me show the concept:
var ts = null;
Date.now = function() {
  if (!ts) return ts = gettimeofday(2);
  return ts;
};
Thread that ticks every millisecond, similar to profiler's thread, just flushes the cache:
{
  ts = null;
}

This way Date.now is cached ad most for 1ms and during this 1ms is always returned from the variable.
new Date() could be then behave like new Date(ts);

That's it.

пятница, 26 октября 2012 г., 8:05:15 UTC+7 пользователь Ben Noordhuis написал:

Ben Noordhuis

unread,
Oct 25, 2012, 11:51:02 PM10/25/12
to nod...@googlegroups.com
When it comes to performance, don't guess or assume - benchmark it.
Clone the repo, try out your changes and see what difference it makes
on benchmark/http_simple or benchmark/http_simple_auto.

Alexey Kupershtokh

unread,
Oct 26, 2012, 3:38:25 AM10/26/12
to nod...@googlegroups.com
Ok then.

cp benchmark/http_simple_auto.js benchmark/http_simple_auto2.js
edit benchmark/http_simple_auto2.js and add this code at the start:
var n = 1000;
var now = Date.now();
Date.now = function() {
  if (n-- == 0) {
    n = 1000;
    now = Date.now();
  }
  return now;
}

Make sure you have a version that already uses Date.now() in timers.js instead of new Date();

RPS for the benchmarks in a few runs in turn, sorted by asc:
benchmark/http_simple_auto.js: 15635, 15739, 16025, 16076, 16091; avg = 15913
benchmark/http_simple_auto2.js: 16402, 16735, 16783, 16815, 16881; avg = 16723
16723 / 15913 = 1.051
So the http_simple_auto2.js is overall 5% faster just because of speeding up Date.now().

пятница, 26 октября 2012 г., 10:51:15 UTC+7 пользователь Ben Noordhuis написал:

Shigeki Ohtsu

unread,
Oct 26, 2012, 5:39:00 AM10/26/12
to nod...@googlegroups.com
I disagree to re-implement Date.now() within Node because it is defined
in ES5 and V8 should take care of its performance.

In timer case, there is an alternative to use process.hrtime() instead
of Date.now(). I've just made a quick hack to use it in

https://github.com/shigeki/node/compare/test_hrtime

for benchmark.

I tested http_simple_bench.sh but there seems no significant difference
as below.

./benchmark/http_simple_bench.sh with Date.now() in timer
ab-hello-world-buffer-102400 Requests per second: 1548.97 [#/sec]
ab-hello-world-buffer-1024 Requests per second: 1755.29 [#/sec]
ab-hello-world-string-102400 Requests per second: 459.39 [#/sec]
ab-hello-world-string-1024 Requests per second: 1919.99 [#/sec]


./benchmark/http_simple_bench.sh with process.hrtime() in timer
ab-hello-world-buffer-102400 Requests per second: 1491.16 [#/sec]
ab-hello-world-buffer-1024 Requests per second: 1748.89 [#/sec]
ab-hello-world-string-102400 Requests per second: 612.75 [#/sec]
ab-hello-world-string-1024 Requests per second: 1953.58 [#/sec]

(2012/10/26 16:38), Alexey Kupershtokh wrote:
> Ok then.
>
> cp benchmark/http_simple_auto.js benchmark/http_simple_auto2.js
> edit benchmark/http_simple_auto2.js and add this code at the start:
> var n = 1000;
> var now = Date.now();
> Date.now = function() {
> if (n-- == 0) {
> n = 1000;
> now = Date.now();
> }
> return now;
> }
>
> Make sure you have a version that already uses Date.now() in timers.js
> instead of new Date();
>
> RPS for the benchmarks in a few runs in turn, sorted by asc:
> benchmark/http_simple_auto.js: 15635, 15739, 16025, 16076, 16091; avg
> = 15913
> benchmark/http_simple_auto2.js: 16402, 16735, 16783, 16815, 16881; avg
> = 16723
> 16723 / 15913 = 1.051
> So the http_simple_auto2.js is overall 5% faster just because of
> speeding up Date.now().
>
> О©╫О©╫О©╫О©╫О©╫О©╫О©╫, 26 О©╫О©╫О©╫О©╫О©╫О©╫О©╫ 2012 О©╫., 10:51:15 UTC+7 О©╫О©╫О©╫О©╫О©╫О©╫О©╫О©╫О©╫О©╫О©╫О©╫ Ben Noordhuis
> О©╫О©╫О©╫О©╫О©╫О©╫О©╫:
> --
> Job Board: http://jobs.nodejs.org/
> Posting guidelines:
> https://github.com/joyent/node/wiki/Mailing-List-Posting-Guidelines
> You received this message because you are subscribed to the Google
> Groups "nodejs" group.
> To post to this group, send email to nod...@googlegroups.com
> To unsubscribe from this group, send email to
> nodejs+un...@googlegroups.com
> For more options, visit this group at
> http://groups.google.com/group/nodejs?hl=en?hl=en

Alexey Kupershtokh

unread,
Oct 26, 2012, 5:48:03 AM10/26/12
to nod...@googlegroups.com
Ok, if re-implementing is not an option, then we could just add optimized version with different name. Or even write an external module. It doesn't matter.

And process.hrtime() is even slower than Date.now();
+new Date               x  1,106,626 ops/sec ±0.80% (99 runs sampled)
Date.now()              x  1,418,551 ops/sec ±0.16% (101 runs sampled)
process.hrtime()        x    833,820 ops/sec ±0.16% (101 runs sampled)
microtime.now()         x  1,247,487 ops/sec ±1.03% (100 runs sampled)

пятница, 26 октября 2012 г., 16:39:14 UTC+7 пользователь shigeki написал:

Trevor Norris

unread,
Oct 26, 2012, 8:10:51 PM10/26/12
to nod...@googlegroups.com
For .hrtime(), are you considering the fact that it's returning an Array, while Date.now() is returning an integer? v8 has some crazy optimizations for the later case. Also, are you considering how much time your actual loop costs or how many runs of Date.now() before incurring 10ms of performance hit?

While there might be a better way, it's so fast already that there are much more important things on the map. Like TLS performance.

Now, I'm not saying it's as fast as it could be. Take the following three cases:

var len = 1e8,
    i = 0;
while (++i <= len) { }

output:

real    0m0.287s
user    0m0.280s
sys     0m0.004s

So I'm doing around 348,432,055/sec. Now the following:

var len = 1e8,
    i = 0;
while (++i <= len) {
    Math.random();
}

output:

real    0m0.881s
user    0m0.872s
sys     0m0.012s

So I'm doing around 125,000,000/sec. Now the final:

var len = 1e8,
    i = 0;

while (++i <= len) {
    Date.now();
}

output:

real    0m4.183s
user    0m4.176s
sys     0m0.016s

Which is around 23,906,287/sec.

So yeah, there might be some performance improvements. But not enough to warrant a re-write of the method.

trev

Alexey Kupershtokh

unread,
Oct 26, 2012, 10:31:17 PM10/26/12
to nod...@googlegroups.com
Trev, Looking at your numbers I totally agree with you.
But I can't repeat these numbers and have the following9\:

wicked@wnote:$ time node 1.js 

real 0m0.273s
user 0m0.260s
sys 0m0.016s
wicked@wnote:~$ time node 2.js 

real 0m0.874s
user 0m0.864s
sys 0m0.012s
wicked@wnote:~$ time node 3.js 

real 2m28.589s
user 0m27.638s
sys 2m2.340s

It's 673,400/sec. Which is around 23,906,287/sec which is 35 times slower than your case :(

In both node 0.8.14 taken from Chris Lee's debs and 0.9.2 compiled on my laptop.
So the question transforms into "why is date.now so damn slow for me"

суббота, 27 октября 2012 г., 7:10:51 UTC+7 пользователь Trevor Norris написал:

niknah

unread,
Oct 27, 2012, 1:26:10 PM10/27/12
to nod...@googlegroups.com
It's pretty quick for me.  I noticed that your timing is 27secs for user, and 2 minutes for system.  When you run it, is there any CPU usage?

Try this on google chrome & some other browser to see if it's a node, v8 or computer related problem...

http://jsperf.com/new-date-value/9

Alexey Kupershtokh

unread,
Oct 27, 2012, 2:21:32 PM10/27/12
to nod...@googlegroups.com
Cpu usage of other proceses is about 3-5%. When node 3.js was running top showed:

Cpu(s): 13.8%us, 41.6%sy,  0.0%ni, 43.6%id,  1.0%wa,  0.0%hi,  0.0%si,  0.0%st

  PID USER      PR  NI  VIRT  RES  SHR S %CPU %MEM    TIME+  COMMAND                                                                                          
12672 wicked    20   0 35192 9104 4204 R  101  0.1   0:42.00 node                                                                                             
17368 wicked    20   0  275m  86m  23m S    2  1.4   5:09.69 chrome   
...

You can find the "Chrome 23.0.1271 (1)" on http://jsperf.com/new-date-value/9 which is much slower than others.
Date.now() is 583,442 only. Very interesting results.
So I must conclude it's not only node-related but the overall v8's Date.now is affected.

wicked@wnote:~$ cat /etc/issue
Ubuntu 12.04.1 LTS \n \l

wicked@wnote:~$ uname -a
Linux wnote 3.2.0-33-generic-pae #52-Ubuntu SMP Thu Oct 18 16:39:21 UTC 2012 i686 i686 i386 GNU/Linux



воскресенье, 28 октября 2012 г., 0:26:10 UTC+7 пользователь niknah написал:

Alexey Kupershtokh

unread,
Oct 27, 2012, 2:29:43 PM10/27/12
to nod...@googlegroups.com
Testing in Firefox 16.0 on Ubuntu
Date.now() is 713,232 /s
Seems it's even system-wide. How to fix this?

Alexey Kupershtokh

unread,
Oct 27, 2012, 2:36:45 PM10/27/12
to nod...@googlegroups.com
PS: My CPU is Intel® Core™ 2 Duo P7350(2GHz, 1 066MHz, 3Mb).

воскресенье, 28 октября 2012 г., 1:30:35 UTC+7 пользователь Alexey Kupershtokh написал:

Alexey Kupershtokh

unread,
Oct 27, 2012, 3:44:05 PM10/27/12
to nod...@googlegroups.com

Alexey Kupershtokh

unread,
Oct 27, 2012, 11:17:28 PM10/27/12
to nod...@googlegroups.com
Testing in Chrome 22.0.1229.94 on Windows Vista:

Using .valueOf() | new Date().valueOf(); | 3,041,709±0.52% | 57% slower
Using .getTime() | new Date().getTime(); | 3,123,610±0.40% | 55% slower
Using +new Date() | +new Date(); |1,945,890±5.94% | 74% slower
Using +new Date | +new Date; |2,050,588±3.42% | 72% slower
Date.now() | Date.now(); | 7,010,134±0.51% | fastest 
Date.now() | dereferenceddatenow(); | 6,995,289±0.81% | fastest

Node.js 0.8.14 installed from official msi:
D:\work\node>node 1.js
Finished in 205 ms, 487,804,878 ops/sec

D:\work\node>node 2.js
Finished in 899 ms, 111,234,705 ops/sec

D:\work\node>node 3.js
Finished in 14412 ms, 6,938,662 ops/sec

It's 10 times faster than in ubuntu.
I have 2 very different:
1) laptop with ubuntu 12.04 x86 with PAE & windows vista
1) PC with ubuntu 11.04 x86 with PAE

So there's a problem with Date.now() in node, chrome and firefox in both ubuntus.

воскресенье, 28 октября 2012 г., 2:44:05 UTC+7 пользователь Alexey Kupershtokh написал:

Alexey Kupershtokh

unread,
Oct 29, 2012, 1:15:26 AM10/29/12
to nod...@googlegroups.com
I've found an extreme solution:

I upgraded ubuntu from
Ubuntu 12.04.1 LTS \n \l;
Linux wnote 3.2.0-33-generic-pae #52-Ubuntu SMP Thu Oct 18 16:39:21 UTC 2012 i686 i686 i386 GNU/Linux
to
Ubuntu 12.10 \n \l;
Linux wnote 3.5.0-17-generic #28-Ubuntu SMP Tue Oct 9 19:31:23 UTC 2012 x86_64 x86_64 x86_64 GNU/Linux

And performance increased 15-20 times to these numbers:

  1. Chrome 22:   10,946,261
  2. FF 16:       15,301,293
  3. Node 0.8.14: 11,671,335

Differences are:
ubuntu: 12.04 -> 12.10
kernel: 3.2.0-33 -> 3.5.0-17
arch: i686 + pae (32 bit) -> x86_64 (64 bit)

I don't know which one matters, but it's a subject for further investigation.

пятница, 26 октября 2012 г., 1:48:50 UTC+7 пользователь Alexey Kupershtokh написал:

Ben Noordhuis

unread,
Oct 29, 2012, 8:38:24 AM10/29/12
to nod...@googlegroups.com
On Mon, Oct 29, 2012 at 6:15 AM, Alexey Kupershtokh
<alexey.ku...@gmail.com> wrote:
> I've found an extreme solution:
>
> I upgraded ubuntu from
> Ubuntu 12.04.1 LTS \n \l;
> Linux wnote 3.2.0-33-generic-pae #52-Ubuntu SMP Thu Oct 18 16:39:21 UTC 2012
> i686 i686 i386 GNU/Linux
> to
> Ubuntu 12.10 \n \l;
> Linux wnote 3.5.0-17-generic #28-Ubuntu SMP Tue Oct 9 19:31:23 UTC 2012
> x86_64 x86_64 x86_64 GNU/Linux
>
> And performance increased 15-20 times to these numbers:
>
> Chrome 22: 10,946,261
> FF 16: 15,301,293
> Node 0.8.14: 11,671,335
>
>
> Differences are:
> ubuntu: 12.04 -> 12.10
> kernel: 3.2.0-33 -> 3.5.0-17
> arch: i686 + pae (32 bit) -> x86_64 (64 bit)
>
> I don't know which one matters, but it's a subject for further
> investigation.

It's probably glibc/VDSO changes. I wager that when you trace your
benchmark with `strace -c gettimeofday,clock_gettime`, you'll see a
ton of hits on your old system and hardly any on your new system.

Alexey Kupershtokh

unread,
Oct 29, 2012, 11:38:37 AM10/29/12
to nod...@googlegroups.com
Did you mean command strace -c -e "gettimeofday,clock_gettime" node 3.js ?

Old (on a more powerful desktop pc):
  PID USER      PR  NI  VIRT  RES  SHR S %CPU %MEM    TIME+  COMMAND            
13220 wicked    20   0  2104  508  428 S   66  0.0   7:46.70 strace             
13221 wicked    20   0 33144 9036 4136 R   41  0.1   4:42.47 node               

wicked@wicked-desktop:~/Alawar$ time strace -c -e "gettimeofday,clock_gettime" node 3.js
^C% time     seconds  usecs/call     calls    errors syscall
------ ----------- ----------- --------- --------- ----------------
100.00    0.799649           0  47083893           gettimeofday
  0.00    0.000000           0         6         1 clock_gettime
------ ----------- ----------- --------- --------- ----------------
100.00    0.799649              47083899         1 total

real 22m28.796s
user 2m5.124s
sys 12m45.256s

As you can see I ctrl+c'ed the process after 22 minutes.

New (on a weaker laptop):
wicked@wnote:~$ time strace -c -e "gettimeofday,clock_gettime" node 3.js 
% time     seconds  usecs/call     calls    errors syscall
------ ----------- ----------- --------- --------- ----------------
  -nan    0.000000           0         3           clock_gettime
------ ----------- ----------- --------- --------- ----------------
100.00    0.000000                     3           total

real 0m8.761s
user 0m8.741s
sys 0m0.016s

The difference is obvious :)

понедельник, 29 октября 2012 г., 19:38:37 UTC+7 пользователь Ben Noordhuis написал:
Reply all
Reply to author
Forward
0 new messages