From 3ac6deefa8112a285fb578875c0aa8eb741e1e23 Mon Sep 17 00:00:00 2001 From: Ryan Dahl Date: Fri, 7 May 2010 19:05:59 -0700 Subject: [PATCH] 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. --- src/node.cc | 122 +++++++++++++++++++++++++++++----------------------- 1 file changed, 67 insertions(+), 55 deletions(-) diff --git a/src/node.cc b/src/node.cc index a4af857075..6e582176f9 100644 --- a/src/node.cc +++ b/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); + 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; + } + } - //fprintf(stderr, "activity, pending: %d\n", pending); + // Otherwise start the gc! - if (pending) { - last_active = ev_now(EV_DEFAULT_UC); - ev_idle_stop(EV_DEFAULT_UC_ &gc_idle); - gc_timer_start(); - } + //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 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