Browse Source

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.
v0.7.4-release
Ryan Dahl 15 years ago
parent
commit
3ac6deefa8
  1. 122
      src/node.cc

122
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);
}
}
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();
ev_timer_stop(&gc_timer);
}
}
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);
// Don't count GC watchers as activity.
tick_times[tick_time_head] = ev_now();
tick_time_head = (tick_time_head + 1) % RPM_SAMPLES;
if (ev_is_pending(&gc_timer)) pending--;
if (ev_is_pending(&gc_idle)) pending--;
if (ev_is_pending(&gc_check)) pending--;
StartGCTimer();
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

Loading…
Cancel
Save