Skip to content

Instantly share code, notes, and snippets.

@ashtuchkin
Created August 15, 2012 19:55
Show Gist options
  • Save ashtuchkin/3363081 to your computer and use it in GitHub Desktop.
Save ashtuchkin/3363081 to your computer and use it in GitHub Desktop.
Node.js "Idle Notification"
// We need to notify V8 when we're idle so that it can run the garbage
// collector. The interface to this is V8::IdleNotification(). It returns
// !! "not" 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.
//
// A rather convoluted algorithm has been devised to determine when Node is
// idle. You'll have to figure it out for yourself.
static uv_check_t gc_check; // -> Check() Called once right after the event loop unblocks.
static uv_idle_t gc_idle; // This is called in loop when the event loop dont have anything to do (until stopped).
static uv_timer_t gc_timer; // Usual timer.
Init:
uv_check_init(uv_default_loop(), &gc_check);
uv_check_start(&gc_check, node::Check);
uv_unref(reinterpret_cast<uv_handle_t*>(&gc_check));
uv_idle_init(uv_default_loop(), &gc_idle);
uv_unref(reinterpret_cast<uv_handle_t*>(&gc_idle));
uv_timer_init(uv_default_loop(), &gc_timer);
uv_unref(reinterpret_cast<uv_handle_t*>(&gc_timer));
#define FAST_TICK 700.
#define GC_WAIT_TIME 5000.
#define RPM_SAMPLES 100
#define TICK_TIME(n) tick_times[(tick_time_head - (n)) % RPM_SAMPLES]
static int64_t tick_times[RPM_SAMPLES]; // Cycle queue of 100 tick times.
static int tick_time_head;
// Called directly after every call to select() (or epoll, or whatever)
static void Check(uv_check_t* watcher, int status) {
assert(watcher == &gc_check);
tick_times[tick_time_head] = uv_now(uv_default_loop());
tick_time_head = (tick_time_head + 1) % RPM_SAMPLES;
StartGCTimer(); // if not started, CheckStatus every 5 seconds.
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 7 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");
uv_idle_start(&gc_idle, node::Idle);
}
static void StartGCTimer () {
if (!uv_is_active((uv_handle_t*) &gc_timer)) {
uv_timer_start(&gc_timer, node::CheckStatus, 5000, 5000);
}
}
static void StopGCTimer () {
if (uv_is_active((uv_handle_t*) &gc_timer)) {
uv_timer_stop(&gc_timer);
}
}
static void CheckStatus(uv_timer_t* watcher, int status) {
assert(watcher == &gc_timer);
// check memory
if (!uv_is_active((uv_handle_t*) &gc_idle)) {
HeapStatistics stats;
V8::GetHeapStatistics(&stats);
if (stats.total_heap_size() > 1024 * 1024 * 128) {
// larger than 128 megs, just start the idle watcher
uv_idle_start(&gc_idle, node::Idle);
return;
}
}
double d = uv_now(uv_default_loop()) - TICK_TIME(3);
//printfb("timer d = %f\n", d);
if (d >= GC_WAIT_TIME - 1.) {
//fprintf(stderr, "start idle\n");
uv_idle_start(&gc_idle, node::Idle);
}
}
static void Idle(uv_idle_t* watcher, int status) {
assert((uv_idle_t*) watcher == &gc_idle);
if (V8::IdleNotification()) { // If true, its done everything it can.
uv_idle_stop(&gc_idle);
StopGCTimer();
}
}
https://github.com/joyent/node/commit/daacb81d3ab8008d0aa3437216df32394e46186e Experimental idle garbage compact
https://github.com/joyent/node/commit/801fb8a614aeafb05235527557d762068e74e6ff Better, faster, idle notification
https://github.com/joyent/node/commit/3ac6deefa8112a285fb578875c0aa8eb741e1e23 Change GC idle notify so that it runs along side setInterval (0.1.94)
Every time after Event Loop unlocks ('check'):
1. If CheckStatus timer not started, setInterval() it every 5 seconds.
2. If in all last 7 ticks, difference between ticks > 0.7 sec
-> Schedule Idle (after this event finishes)
CheckStatus:
1. If HeapTotal > 128Mb
-> Schedule Idle (after this event finishes)
2. If 2-nd to last tick was > 5 sec ago (originally meant to be 4 sec ago)
-> Schedule Idle (after this event finishes)
Idle (after the event queue is done, repeatedly, but checking the handles/requests/timers):
IdleNotification. If no more work:
-> Unschedule CheckStatus
-> Stop Idling
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment