Change GC idle notify so that it runs along side setInterval
Doesn't appear any slower. Also checks for high memory usage and tries to force more notifications.
This commit is contained in:
parent
bc45adcffa
commit
3ac6deefa8
124
src/node.cc
124
src/node.cc
@ -93,88 +93,73 @@ static ev_idle eio_poller;
|
||||
// true if the heap hasn't be fully compacted, and needs to be run again.
|
||||
// Returning false means that it doesn't have anymore work to do.
|
||||
//
|
||||
// We try to wait for a period of GC_INTERVAL (2 seconds) of idleness, where
|
||||
// idleness means that no libev watchers have been executed. Since
|
||||
// everything in node uses libev watchers, this is a pretty good measure of
|
||||
// idleness. This is done with gc_check, which records the timestamp
|
||||
// last_active on every tick of the event loop, and with gc_timer which
|
||||
// executes every few seconds to measure if
|
||||
// last_active + GC_INTERVAL < ev_now()
|
||||
// If we do find a period of idleness, then we start the gc_idle timer which
|
||||
// will very repaidly call IdleNotification until the heap is fully
|
||||
// compacted.
|
||||
static ev_tstamp last_active;
|
||||
static ev_timer gc_timer;
|
||||
// A rather convoluted algorithm has been devised to determine when Node is
|
||||
// idle. You'll have to figure it out for yourself.
|
||||
static ev_check gc_check;
|
||||
static ev_idle gc_idle;
|
||||
#define GC_INTERVAL 1.0
|
||||
static ev_timer gc_timer;
|
||||
bool need_gc;
|
||||
|
||||
static void gc_timer_start () {
|
||||
|
||||
#define FAST_TICK 0.7
|
||||
#define GC_WAIT_TIME 5.
|
||||
#define RPM_SAMPLES 100
|
||||
#define TICK_TIME(n) tick_times[(tick_time_head - (n)) % RPM_SAMPLES]
|
||||
static ev_tstamp tick_times[RPM_SAMPLES];
|
||||
static int tick_time_head;
|
||||
|
||||
static void StartGCTimer () {
|
||||
if (!ev_is_active(&gc_timer)) {
|
||||
ev_timer_start(EV_DEFAULT_UC_ &gc_timer);
|
||||
ev_unref(EV_DEFAULT_UC);
|
||||
}
|
||||
}
|
||||
|
||||
static void gc_timer_stop () {
|
||||
static void StopGCTimer () {
|
||||
if (ev_is_active(&gc_timer)) {
|
||||
ev_ref(EV_DEFAULT_UC);
|
||||
ev_timer_stop(EV_DEFAULT_UC_ &gc_timer);
|
||||
ev_timer_stop(&gc_timer);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void CheckIdleness(EV_P_ ev_timer *watcher, int revents) {
|
||||
assert(watcher == &gc_timer);
|
||||
assert(revents == EV_TIMER);
|
||||
|
||||
//fprintf(stderr, "check idle\n");
|
||||
|
||||
ev_tstamp idle_time = ev_now(EV_DEFAULT_UC) - last_active;
|
||||
|
||||
if (idle_time > GC_INTERVAL) {
|
||||
if (!V8::IdleNotification()) {
|
||||
ev_idle_start(EV_DEFAULT_UC_ &gc_idle);
|
||||
}
|
||||
gc_timer_stop();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void NotifyIdleness(EV_P_ ev_idle *watcher, int revents) {
|
||||
static void Idle(EV_P_ ev_idle *watcher, int revents) {
|
||||
assert(watcher == &gc_idle);
|
||||
assert(revents == EV_IDLE);
|
||||
|
||||
//fprintf(stderr, "notify idle\n");
|
||||
//fprintf(stderr, "idle\n");
|
||||
|
||||
if (V8::IdleNotification()) {
|
||||
ev_idle_stop(EV_A_ watcher);
|
||||
gc_timer_stop();
|
||||
StopGCTimer();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void Activity(EV_P_ ev_check *watcher, int revents) {
|
||||
// Called directly after every call to select() (or epoll, or whatever)
|
||||
static void Check(EV_P_ ev_check *watcher, int revents) {
|
||||
assert(watcher == &gc_check);
|
||||
assert(revents == EV_CHECK);
|
||||
|
||||
int pending = ev_pending_count(EV_DEFAULT_UC);
|
||||
tick_times[tick_time_head] = ev_now();
|
||||
tick_time_head = (tick_time_head + 1) % RPM_SAMPLES;
|
||||
|
||||
// Don't count GC watchers as activity.
|
||||
StartGCTimer();
|
||||
|
||||
if (ev_is_pending(&gc_timer)) pending--;
|
||||
if (ev_is_pending(&gc_idle)) pending--;
|
||||
if (ev_is_pending(&gc_check)) pending--;
|
||||
|
||||
assert(pending >= 0);
|
||||
|
||||
//fprintf(stderr, "activity, pending: %d\n", pending);
|
||||
|
||||
if (pending) {
|
||||
last_active = ev_now(EV_DEFAULT_UC);
|
||||
ev_idle_stop(EV_DEFAULT_UC_ &gc_idle);
|
||||
gc_timer_start();
|
||||
for (int i = 0; i < (int)(GC_WAIT_TIME/FAST_TICK); i++) {
|
||||
double d = TICK_TIME(i+1) - TICK_TIME(i+2);
|
||||
//printf("d = %f\n", d);
|
||||
// If in the last 5 ticks the difference between
|
||||
// ticks was less than 0.7 seconds, then continue.
|
||||
if (d < FAST_TICK) {
|
||||
//printf("---\n");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Otherwise start the gc!
|
||||
|
||||
//fprintf(stderr, "start idle 2\n");
|
||||
ev_idle_start(EV_A_ &gc_idle);
|
||||
}
|
||||
|
||||
|
||||
@ -1374,6 +1359,34 @@ error:
|
||||
}
|
||||
#endif // __linux__
|
||||
|
||||
|
||||
static void CheckStatus(EV_P_ ev_timer *watcher, int revents) {
|
||||
assert(watcher == &gc_timer);
|
||||
assert(revents == EV_TIMER);
|
||||
|
||||
#if HAVE_GETMEM
|
||||
// check memory
|
||||
size_t rss, vsize;
|
||||
if (!ev_is_active(&gc_idle) && getmem(&rss, &vsize) == 0) {
|
||||
if (rss > 1024*1024*128) {
|
||||
// larger than 128 megs, just start the idle watcher
|
||||
ev_idle_start(EV_A_ &gc_idle);
|
||||
return;
|
||||
}
|
||||
}
|
||||
#endif // HAVE_GETMEM
|
||||
|
||||
double d = ev_now() - TICK_TIME(3);
|
||||
|
||||
//printfb("timer d = %f\n", d);
|
||||
|
||||
if (d >= GC_WAIT_TIME - 1.) {
|
||||
//fprintf(stderr, "start idle\n");
|
||||
ev_idle_start(EV_A_ &gc_idle);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
v8::Handle<v8::Value> MemoryUsage(const v8::Arguments& args) {
|
||||
HandleScope scope;
|
||||
assert(args.Length() == 0);
|
||||
@ -2040,13 +2053,12 @@ int main(int argc, char *argv[]) {
|
||||
|
||||
ev_idle_init(&node::tick_spinner, node::Spin);
|
||||
|
||||
ev_timer_init(&node::gc_timer, node::CheckIdleness, 2*GC_INTERVAL, 2*GC_INTERVAL);
|
||||
|
||||
ev_check_init(&node::gc_check, node::Activity);
|
||||
ev_check_init(&node::gc_check, node::Check);
|
||||
ev_check_start(EV_DEFAULT_UC_ &node::gc_check);
|
||||
ev_unref(EV_DEFAULT_UC);
|
||||
|
||||
ev_idle_init(&node::gc_idle, node::NotifyIdleness);
|
||||
ev_idle_init(&node::gc_idle, node::Idle);
|
||||
ev_timer_init(&node::gc_timer, node::CheckStatus, 5., 5.);
|
||||
|
||||
|
||||
// Setup the EIO thread pool
|
||||
|
Loading…
x
Reference in New Issue
Block a user