Skip to content

loop,threadpool: collect statistics#1764

Closed
jasnell wants to merge 6 commits into
libuv:masterfrom
jasnell:loop-stats-redo
Closed

loop,threadpool: collect statistics#1764
jasnell wants to merge 6 commits into
libuv:masterfrom
jasnell:loop-stats-redo

Conversation

@jasnell

@jasnell jasnell commented Feb 28, 2018

Copy link
Copy Markdown
Contributor

This is a combined reworking of #1489 (loop stats) and #1528 (threadpool stats) to use a common API pattern. Enabling stats collection is accomplished via uv_loop_configure() for both.

The loop stats impl is simplified to remove the sampling configuration. The callback is triggered at the end of every loop tick. It's up to the receiver to figure out how often they want to pay attention to it.

Stats collection for both the loop and threadpool may be enabled/disabled dynamically after the loop is started.

Comment thread src/unix/core.c
if ((mode == UV_RUN_ONCE && !ran_pending) || mode == UV_RUN_DEFAULT)
timeout = uv_backend_timeout(loop);

uv__update_stats_ts(loop, poll_start);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

is there something that does not allow us to put a stats regarding the timeout used during the current tick?

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.

We certainly could add that... I'm curious about the use case for it but it would be trivial to add.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

In case the poll time is greater than timeout this might indicate that the CPU was too busy giving the CPU time to another process. So, the timeout could be used to apply something like that:

sleep_time = min(timeout, poll_time)

So, yes it's a nice to have, but having it at hand it's too kindy to don't ask for it :)

Comment thread docs/src/loop.rst Outdated

Type definition for callback passed to :c:func:`uv_walk`.

.. c::type:: void (*uv_stats_cb)(uv_loop_stats_data_t* stats, void* data)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

One too many colons between c and type. Ditto below

@jasnell

jasnell commented Feb 28, 2018

Copy link
Copy Markdown
Contributor Author

@pfreixes @TimothyGu ... both issues fixed.

@pfreixes

pfreixes commented Mar 1, 2018

Copy link
Copy Markdown

Thanks for the work done! Good to see this feature moving forward.

Comment thread .vscode/launch.json Outdated
@@ -0,0 +1,19 @@
{

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Can you remove this file please.

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.

sigh.. oops...

@jasnell

jasnell commented Mar 4, 2018

Copy link
Copy Markdown
Contributor Author

@cjihrig ... fixed...

btw, I intend for all the commits to be squashed into one when this lands.

@pfreixes

pfreixes commented Apr 2, 2018

Copy link
Copy Markdown

Hi libuv members - @cjihrig @saghul @bnoordhuis and so on.

I would like to use the characteristics that this PR introduces, allowing the developers to make some profiling and getting statistics of the loop performance.

So, basically my question is: which are the plans for the v2 and specifically for that PR?

Thanks,

@santigimeno

Copy link
Copy Markdown
Member

So, basically my question is: which are the plans for the v2 and specifically for that PR?

I would say that, most probably, v2 is not going to happen soon. See: #1597.

@jasnell

jasnell commented Apr 2, 2018

Copy link
Copy Markdown
Contributor Author

Would definitely like to know what else this needs before it can land. I'd definitely like to make sure it's part of 2.0, even if 2.0 is going to take a while.

@jasnell

jasnell commented Apr 19, 2018

Copy link
Copy Markdown
Contributor Author

Ping @cjihrig @saghul @bnoordhuis ... is there anything else needed to get this landed?

@cjihrig

cjihrig commented Apr 19, 2018

Copy link
Copy Markdown
Contributor

I haven't forgotten about this. I've been removing and landing things from the v2 PR backlog. It's down to 8 (with 2 ready to land very soon) from 14 two weeks ago. I've been trying to prioritize the things that are several years old already 😄

@jasnell

jasnell commented Apr 19, 2018

Copy link
Copy Markdown
Contributor Author

Awesome. I appreciate the work you are doing on getting things caught up. Just wanted to make sure there wasn't something else blocking this :)

@bnoordhuis

Copy link
Copy Markdown
Member

I've been thinking: since Node.js uses Chromium's trace_events now, wouldn't it make sense to start using that in libuv too? I mean, this PR is mostly fine but ultimately it's to feed data into trace_events in a roundabout way.

@jasnell

jasnell commented Apr 19, 2018

Copy link
Copy Markdown
Contributor Author

That's the plan here, to be honest. Once integrated into node.js, I'll have a PR that kicks this data out to both the trace events stream and out to the perf API.

I suppose we could integrate the trace event macros directly into libuv but that would add a dependency that's really not strictly necessary and we would need to decide if it makes sense to pull in the complete machinery for outputting the trace event file.

@pfreixes

Copy link
Copy Markdown

I can not speak on their behalf but I guess that other libuv dependencies such as gevent, uvloop, u others will feel more comfortable working with the raw events provided by this MR.

@bnoordhuis

Copy link
Copy Markdown
Member

need to decide if it makes sense to pull in the complete machinery for outputting the trace event file

Libuv is a library so no, that would be the responsibility of the embedder. Libuv just needs to make the events available in a way that's easily consumable.

other libuv dependencies such as gevent, uvloop, u others will feel more comfortable working with the raw events provided by this MR.

MR = PR? trace_events is in many ways 'rawer' than what this PR provides.

@jasnell

jasnell commented Apr 19, 2018

Copy link
Copy Markdown
Contributor Author

Well, I think either way we'd end up in roughly the same place but we could take an approach of invoking a callback at each collection point rather than setting a timestamp in a struct. Either way works for me, I'm more interested in getting the data out somehow than in any one specific way :-)

@jasnell

jasnell commented Apr 19, 2018

Copy link
Copy Markdown
Contributor Author

In other words, I'm happy to rework this PR in any way you think would be better @bnoordhuis :-)

@pfreixes

Copy link
Copy Markdown

Is not libuv a library used by many other technologies? Changes like that would suppose force these tecnologies to use a specific dependency to make usage of the tracing system?

If this is the case, I would prefer to keep the current aproach.

@bnoordhuis

Copy link
Copy Markdown
Member

@cjihrig @santigimeno @saghul WDYT?

@cjihrig

cjihrig commented Apr 19, 2018

Copy link
Copy Markdown
Contributor

What exactly are the options on the table? I'm not really keen on the idea of integrating directly with trace_events or anything else from the Chromium universe if it can be avoided.

@santigimeno

Copy link
Copy Markdown
Member

TBH I'm not familiar with the trace_events at all and the current proposal looks pretty generic which I like. Are there other advantages to of using trace_events apart from easier integration with node?

@jasnell

jasnell commented Apr 19, 2018

Copy link
Copy Markdown
Contributor Author

If I'm understanding @bnoordhuis correctly, the options on the table are:

For the loop stats:

  1. Collect timestamps in a struct as the tick proceeds, invoke a callback once at the end of the tick (which is what this PR does)

  2. Invoke a callback at each collection point (rather than grab the timestamp).

The difference between the two is that 1 requires a storage struct and a single callback per tick, where as 2 means we don't need to store timestamps but have multiple callbacks per tick.

For the threadpool stats, that would remain the same as implemented here.

@santigimeno

Copy link
Copy Markdown
Member

@jasnell thanks for the explanation. Could you elaborate a little where trace_events come into play?

@jasnell

jasnell commented Apr 19, 2018

Copy link
Copy Markdown
Contributor Author

The only actual trace events integration here would be on the embedder-side. E.g. Node.js would register callbacks that would emit v8 trace events... Which we can easily do with either of the two approaches. 1 records the events after they happen at the end of the tick, 2 records the events as they happen. 2 would have the advantage of at least recording partial information should the process exit in the middle of the tick.

@santigimeno

Copy link
Copy Markdown
Member

@jasnell thanks.

I think I like option 2 better.

@bnoordhuis

Copy link
Copy Markdown
Member

Another advantage of trace_events is that we won't have to worry about ABI. An issue with this PR is that we can't easily add or change events without touching the struct.

@jasnell

jasnell commented Apr 19, 2018

Copy link
Copy Markdown
Contributor Author

Works for me. I'll get this reworked to drop the struct and use the 2nd approach.

@pfreixes

Copy link
Copy Markdown

So, ita just matter of change the pattern? Calling the callbacks at each event? If this is the case it wil suit for my proposals too

@jasnell

jasnell commented Apr 20, 2018

Copy link
Copy Markdown
Contributor Author

@bnoordhuis @santigimeno @saghul @cjihrig @evanlucas ....

Ok, so what I'm going to do is open a second PR that uses a more trace events like approach as discussed above. Rather than collecting timestamps into a struct it will invoke a trace callback as the loop progresses.

Before I get too far into the implementation, I want to solicit some opinions on the API because there are a couple ways I could do it.

Option 1: One generic trace callback

Internal function:

uv__trace(uv_loop_t* loop, uv_trace_type type, const char* name, ...);

In the loop:

for (...) { // in uv_run
  // ...
  uv__trace(loop, UV_TRACE_TYPE_START, "tick");

  uv__trace(loop, UV_TRACE_TYPE_START, "timers");
  count = uv_run_timers(...)
  uv__trace(loop, UV_TRACE_TYPE_END, "timers", "count", count);

  uv__trace(loop, UV_TRACE_TYPE_END, "tick");
  // ...
}

On the embedder side:

static void trace_cb(uv_trace_type* type, const char* name, void* data, va_list) {
  // ...
}

// ...
uv_trace_t config;
config.cb = trace_cb;
config.data = &loop;

uv_loop_config(&loop, UV_LOOP_TRACE, &config);
uv_run(...);

The key bit with this option is that a single callback is configured. Notice, however, the use of va_list in the callback signature. This mimics the V8 Trace Events macros that allow an arbitrary number of additional named arguments to be passed through. Unfortunately, doing it this way means the embedder will need to know how many named arguments and the types associated with those based on the name and type of the event being emitted. It would not be obvious in the API.

Therefore we have option 2: event-specific callbacks

On the libuv side:

for (...) { // in uv_run
  uv_trace_start(loop, UV_TRACE_PHASE_TICK);

  uv_trace_start(loop, UV_TRACE_PHASE_TIMERS);
  count = uv_run_timers(...);
  uv_trace_end(loop, UV_TRACE_PHASE_TIMERS, count);
  // ...
  uv_trace_end(loop, UV_TRACE_PHASE_TICK);
}

On the embedder side:

static void trace_start_cb(uv_trace_phase phase, void* data) {}
static void trace_end_cb(uv_trace_phase phase, size_t count, void* data) {}

uv_trace_t config;
config.start_cb = trace_start_cb;
config.end_cb = trace_end_cb;
config.data = &loop;

With this approach, the API is less ambiguous, more rigid, but also less flexible in terms of what data is passed through.

Which approach would each of you prefer?

@jasnell

jasnell commented Apr 20, 2018

Copy link
Copy Markdown
Contributor Author

There are other ways of doing this, of course... but the key question is how we should handle the arbitrary additional bits of information that should be included in the trace call. The poll event, for instance, should include the timeout, the various check|prepare|idle|timer end events should include the count, etc. If we go too rigid with it, then it would break ABI if we decide we need to add more info later. If we go too loose with it, the API becomes ambiguous.

@jasnell

jasnell commented Apr 23, 2018

Copy link
Copy Markdown
Contributor Author

Ok, see #1815 for an alternative take based around a trace events model.

@davisjam davisjam mentioned this pull request Aug 27, 2018
@jasnell

jasnell commented Sep 19, 2018

Copy link
Copy Markdown
Contributor Author

Closing in favor of #1815

@jasnell jasnell closed this Sep 19, 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