Skip to content

loop: add uv_loop_stats - #1489

Closed
jasnell wants to merge 1 commit into
libuv:masterfrom
jasnell:loop-stats
Closed

jasnell wants to merge 1 commit into
libuv:masterfrom
jasnell:loop-stats

Conversation

@jasnell

@jasnell jasnell commented Aug 18, 2017

Copy link
Copy Markdown
Contributor

This is a bit of speculative work in support of nodejs/node#14680.

Adds uv_loop_stats_t and uv_loop_stats() APIs that provide high resolution timing for event loop phases. This approach introduces significantly less latency than using callback handles for the same purpose, and gives more precise results.

/cc @matthewloring

Comment thread include/uv-unix.h Outdated
uv__io_t signal_io_watcher; \
uv_signal_t child_watcher; \
int emfile_fd; \
uv_loop_stats_t stats; \

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This breaks the ABI, so we can't land it on v1.x.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah, was a bit afraid of that.

Comment thread docs/src/loop.rst Outdated
invalid. That function must be called again to determine the
correct backend file descriptor.

.. c:function:: uv_loop_stats_t* uv_loop_stats(uv_loop_t* loop)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this function should copy the stats instead of returning a pointer to an internal field.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1 ... was wanting to avoid the copying if at all possible and went back and forth on this. Copying works.

Comment thread include/uv.h Outdated
char* homedir;
};

struct uv_loop_stats_s {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what about async handles?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

still thinking through those

@saghul

saghul commented Aug 18, 2017

Copy link
Copy Markdown
Member

What is the desired information here? I see a bunch of _start fields, but no _stop. Also, currently they are pretty much all going to match the loop iteration count, because they run on every loop iteration.

Can you please elaborate on what are the desired metrics?

Last, this breaks the ABI, so it should be targeted at master.

@jasnell

jasnell commented Aug 18, 2017

Copy link
Copy Markdown
Contributor Author

What is the desired information here?

Specifically the hr timestamps when each of the key phases kicks off. The original iteration of this included _stops but they were so close to the subsequent _starts that they were useless. The goal is to be able to generate precise measurements of latency between phases and knowing precisely when those start is generally good enough.

Whether this is the right approach to take, I'm not yet sure. I'd much prefer to take an approach that doesn't break ABI but I'm still looking at it.

@saghul

saghul commented Aug 18, 2017

Copy link
Copy Markdown
Member

The original iteration of this included _stops but they were so close to the subsequent _starts that they were useless. The goal is to be able to generate precise measurements of latency between phases and knowing precisely when those start is generally good enough.

This makes assumptions about the order of phases, which is something I really don't want people to rely on. Also, about them being so close: this could be used as a mechanism to tell the user that too much time was spent in a callback (I can imagine some computations taking too long on a timer, or a sync fs operation,...).

@jasnell

jasnell commented Aug 18, 2017

Copy link
Copy Markdown
Contributor Author

Makes sense. Starts and stops for each phase then, without any assumption about ordering.

@jasnell
jasnell changed the base branch from v1.x to master August 18, 2017 20:50
@jasnell

jasnell commented Aug 18, 2017

Copy link
Copy Markdown
Contributor Author

@saghul @mcollina ... PTAL

I've refactored this significantly from the first take and rebased the PR against master.

This enables two basic models for getting stats data: sync and async. The sync is pretty straight forward:

  uv_stats_info_t info;
  uv_loop_stats(uv_default_loop(), &info);

  printf("loop enter: %llu\n", info.loop_enter);
  printf("loop exit: %llu\n", info.loop_exit);

The async uses uv_loop_configure per @saghul's suggestion:

void on_stats(uv_stats_info_t* info) { /** ... **/ }

uv_stats_config_t config = { UV_LOOP_STATS_TICK, 0, on_stats };
uv_loop_configure(uv_default_loop(), UV_LOOP_STATS, &config);
uv_run(uv_default_loop(), UV_RUN_DEFAULT);

There are three scheduling options configured using the uv_stats_config_t struct:

  • UV_LOOP_STATS_TICK - notify on every turn
  • UV_LOOP_STATS_COUNT - notify on every Nth turn
  • UV_LOOP_STATS_TIME - notify every Nth nanosecond

For now the start and end timestamps for each loop phase, the enter and exit timestamps for the loop, and the number of loop turns are being tracked. We can add aditional measures (such as the number of pending events) fairly easily.

@santigimeno santigimeno added the v2 label Aug 19, 2017
@mcollina

Copy link
Copy Markdown

I'm really 👍 with this, too bad it needs to wait for libuv 2.

@jasnell

jasnell commented Aug 21, 2017

Copy link
Copy Markdown
Contributor Author

I would really like to try to find a way of doing this that doesn't break ABI. If we can find a non-terrible and efficient way of having a separate loop->stats map that doesn't require adding information to uv_loop_t, then it's possible.

@santigimeno

Copy link
Copy Markdown
Member

There's this data field that might be used? Not sure if it's a good idea though

@saghul

saghul commented Aug 25, 2017

Copy link
Copy Markdown
Member

Not sure if it's a good idea though

That field is for users, not our own internal use.

@pfreixes

pfreixes commented Aug 25, 2017

Copy link
Copy Markdown

Hi @jasnell I was keen on implementing a feature to measure the loop load [1], @saghul told me that this feature might be built on top of your PR. I definitely believe that yes, it can be built on top of that feature. But I'm missing one statistic.

To calculate a more precise load its needed use the maximum time spent polling IO between the timeout and the real time used by the OS. When the OS does not schedule the loop after the timeout time means that system was enough busy to don't give the CPU at that moment, it becomes especially important when the CPU is highly demanded by other processes that share the same CPU to the process that owns the loop. Therefore will be needed also the timeout value that was initially asked by the loop.

Having this PR, the code needed to calculate the load can be done in the userland. Therefore, I will focus on the userland code. I will try to give you a proof of concept implementation, let's see how degrades the performance having the accumulative phase in the cb. This might be my only concern, having in mind that is needed to use the UV_LOOP_STATS_TICK.

BTW I will like to see this change in the 1.X series.

[1] https://groups.google.com/forum/#!topic/libuv/RVtgltLI_fE

@pfreixes

pfreixes commented Aug 25, 2017

Copy link
Copy Markdown

@jasnell I've managed to implement a userland code written in Cython that uses your feature, looks promising. Two comments.

The stats callback should provide as an argument the loop instance, this will give the chance to the user to retrieve the context data that is set by the user once the loop is initialized in the data attribute.

About performance, using Cython the degradation experimented using the UV_LOOP_STATS_TICK is less than 3%. Having in mind that peak reached in my computer is almost 300K coroutines per second I believe that this is much more than it would be needed in a Python environment. Therefore I can implement the load strategy on top of this feature.

PD: remember what I said about the timeout, if it might be saved as an attribute of the stats this will help a lot.

sam-github added a commit to sam-github/node that referenced this pull request Sep 27, 2017
The node frame (aka loop) timing API did not land, it depends on
libuv/libuv#1489 which is still a WIP.

See: nodejs#14680 (comment)
jasnell pushed a commit to nodejs/node that referenced this pull request Sep 29, 2017
The node frame (aka loop) timing API did not land, it depends on
libuv/libuv#1489 which is still a WIP.

See: #14680 (comment)

PR-URL: #15641
Reviewed-By: Colin Ihrig <[email protected]>
Reviewed-By: Timothy Gu <[email protected]>
Reviewed-By: James M Snell <[email protected]>
Reviewed-By: Luigi Pinca <[email protected]>
Reviewed-By: Jeremiah Senkpiel <[email protected]>
Reviewed-By: Ruben Bridgewater <[email protected]>
MylesBorins pushed a commit to nodejs/node that referenced this pull request Sep 29, 2017
The node frame (aka loop) timing API did not land, it depends on
libuv/libuv#1489 which is still a WIP.

See: #14680 (comment)

PR-URL: #15641
Reviewed-By: Colin Ihrig <[email protected]>
Reviewed-By: Timothy Gu <[email protected]>
Reviewed-By: James M Snell <[email protected]>
Reviewed-By: Luigi Pinca <[email protected]>
Reviewed-By: Jeremiah Senkpiel <[email protected]>
Reviewed-By: Ruben Bridgewater <[email protected]>
addaleax pushed a commit to addaleax/ayo that referenced this pull request Sep 30, 2017
The node frame (aka loop) timing API did not land, it depends on
libuv/libuv#1489 which is still a WIP.

See: nodejs/node#14680 (comment)

PR-URL: nodejs/node#15641
Reviewed-By: Colin Ihrig <[email protected]>
Reviewed-By: Timothy Gu <[email protected]>
Reviewed-By: James M Snell <[email protected]>
Reviewed-By: Luigi Pinca <[email protected]>
Reviewed-By: Jeremiah Senkpiel <[email protected]>
Reviewed-By: Ruben Bridgewater <[email protected]>
MylesBorins pushed a commit to nodejs/node that referenced this pull request Oct 3, 2017
The node frame (aka loop) timing API did not land, it depends on
libuv/libuv#1489 which is still a WIP.

See: #14680 (comment)

PR-URL: #15641
Reviewed-By: Colin Ihrig <[email protected]>
Reviewed-By: Timothy Gu <[email protected]>
Reviewed-By: James M Snell <[email protected]>
Reviewed-By: Luigi Pinca <[email protected]>
Reviewed-By: Jeremiah Senkpiel <[email protected]>
Reviewed-By: Ruben Bridgewater <[email protected]>
MylesBorins pushed a commit to nodejs/node that referenced this pull request Oct 3, 2017
The node frame (aka loop) timing API did not land, it depends on
libuv/libuv#1489 which is still a WIP.

See: #14680 (comment)

PR-URL: #15641
Reviewed-By: Colin Ihrig <[email protected]>
Reviewed-By: Timothy Gu <[email protected]>
Reviewed-By: James M Snell <[email protected]>
Reviewed-By: Luigi Pinca <[email protected]>
Reviewed-By: Jeremiah Senkpiel <[email protected]>
Reviewed-By: Ruben Bridgewater <[email protected]>
@bnoordhuis

Copy link
Copy Markdown
Member

This PR needs a rebase and overlaps to some extent with #1528. The APIs can probably be unified a little.

@jasnell

jasnell commented Oct 5, 2017

Copy link
Copy Markdown
Contributor Author

Will definitely take a look when I'm back from node interactive. Would make a good activity for the flight home.

Any particular concerns beyond reconciling with #1528?

MylesBorins pushed a commit to nodejs/node that referenced this pull request Oct 11, 2017
The node frame (aka loop) timing API did not land, it depends on
libuv/libuv#1489 which is still a WIP.

See: #14680 (comment)

PR-URL: #15641
Reviewed-By: Colin Ihrig <[email protected]>
Reviewed-By: Timothy Gu <[email protected]>
Reviewed-By: James M Snell <[email protected]>
Reviewed-By: Luigi Pinca <[email protected]>
Reviewed-By: Jeremiah Senkpiel <[email protected]>
Reviewed-By: Ruben Bridgewater <[email protected]>
@pfreixes

Copy link
Copy Markdown

Yeps, any news about that @jasnell? I would like to see this MR moving ahead :) but having also a new metric with the timeout used during the IO poll, somehting that should be a trivial change.

@jasnell

jasnell commented Feb 27, 2018

Copy link
Copy Markdown
Contributor Author

Updating this now. Will work on it with a separate PR, however.

@pfreixes

Copy link
Copy Markdown

@jasnell another thing that I forgot to mention

The stats callback should provide as an argument the loop instance, this will give the chance to the user to retrieve the context data that is set by the user once the loop is initialized in the data attribute.

@jasnell

jasnell commented Feb 28, 2018

Copy link
Copy Markdown
Contributor Author

Replaced by #1764

@jasnell jasnell closed this Feb 28, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants