Created
August 15, 2012 19:55
-
-
Save ashtuchkin/3363081 to your computer and use it in GitHub Desktop.
Node.js "Idle Notification"
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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(); | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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