summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRyan Dahl <ry@tinyclouds.org>2010-05-07 19:05:59 -0700
committerRyan Dahl <ry@tinyclouds.org>2010-05-07 19:06:05 -0700
commit3ac6deefa8112a285fb578875c0aa8eb741e1e23 (patch)
treea35b2c180ac539eb7d5dfc496e1a721cdef50a6e
parentbc45adcffac675a423d4d0e5a765650d73de952c (diff)
downloadnodejs-3ac6deefa8112a285fb578875c0aa8eb741e1e23.tar.gz
nodejs-3ac6deefa8112a285fb578875c0aa8eb741e1e23.tar.bz2
nodejs-3ac6deefa8112a285fb578875c0aa8eb741e1e23.zip
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.
-rw-r--r--src/node.cc122
1 files changed, 67 insertions, 55 deletions
diff --git a/src/node.cc b/src/node.cc
index a4af85707..6e582176f 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<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