Commit 90c9f1b9 authored by vitalyr@chromium.org's avatar vitalyr@chromium.org

Suspend runtime profiler as soon as we exit JS.

Lots of web pages have really frequently firing timers that keep the
profiler thread spinning if we require a period of JS inactivity
before suspending the profiler. While it's possible to throttle it by
increasing the sleep delay and adjusting the duration of the required
inactive period, it seemed much simpler to just stop it immediately on
exiting JS.

Stopping the profiler this way effectively turned off two optimization
heuristics: 1) eager optimization (it's reset on waking up the
profiler and now the profiler wakes up much more frequently) and 2)
optimization throttling based on JS to non-JS state ratio (the ratio
is now 100%). I removed these two heuristics and found no performance
regressions so far.

R=ager@chromium.org
BUG=crbug.com/77625
TEST=none

Review URL: http://codereview.chromium.org/7274024

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@8472 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
parent d2f08851
......@@ -52,8 +52,7 @@ CompilationCache::CompilationCache(Isolate* isolate)
eval_global_(isolate, kEvalGlobalGenerations),
eval_contextual_(isolate, kEvalContextualGenerations),
reg_exp_(isolate, kRegExpGenerations),
enabled_(true),
eager_optimizing_set_(NULL) {
enabled_(true) {
CompilationSubCache* subcaches[kSubCacheCount] =
{&script_, &eval_global_, &eval_contextual_, &reg_exp_};
for (int i = 0; i < kSubCacheCount; ++i) {
......@@ -62,10 +61,7 @@ CompilationCache::CompilationCache(Isolate* isolate)
}
CompilationCache::~CompilationCache() {
delete eager_optimizing_set_;
eager_optimizing_set_ = NULL;
}
CompilationCache::~CompilationCache() {}
static Handle<CompilationCacheTable> AllocateTable(Isolate* isolate, int size) {
......@@ -457,47 +453,6 @@ void CompilationCache::PutRegExp(Handle<String> source,
}
static bool SourceHashCompare(void* key1, void* key2) {
return key1 == key2;
}
HashMap* CompilationCache::EagerOptimizingSet() {
if (eager_optimizing_set_ == NULL) {
eager_optimizing_set_ = new HashMap(&SourceHashCompare);
}
return eager_optimizing_set_;
}
bool CompilationCache::ShouldOptimizeEagerly(Handle<JSFunction> function) {
if (FLAG_opt_eagerly) return true;
uint32_t hash = function->SourceHash();
void* key = reinterpret_cast<void*>(hash);
return EagerOptimizingSet()->Lookup(key, hash, false) != NULL;
}
void CompilationCache::MarkForEagerOptimizing(Handle<JSFunction> function) {
uint32_t hash = function->SourceHash();
void* key = reinterpret_cast<void*>(hash);
EagerOptimizingSet()->Lookup(key, hash, true);
}
void CompilationCache::MarkForLazyOptimizing(Handle<JSFunction> function) {
uint32_t hash = function->SourceHash();
void* key = reinterpret_cast<void*>(hash);
EagerOptimizingSet()->Remove(key, hash);
}
void CompilationCache::ResetEagerOptimizingData() {
HashMap* set = EagerOptimizingSet();
if (set->occupancy() > 0) set->Clear();
}
void CompilationCache::Clear() {
for (int i = 0; i < kSubCacheCount; i++) {
subcaches_[i]->Clear();
......
......@@ -223,14 +223,6 @@ class CompilationCache {
JSRegExp::Flags flags,
Handle<FixedArray> data);
// Support for eager optimization tracking.
bool ShouldOptimizeEagerly(Handle<JSFunction> function);
void MarkForEagerOptimizing(Handle<JSFunction> function);
void MarkForLazyOptimizing(Handle<JSFunction> function);
// Reset the eager optimization tracking data.
void ResetEagerOptimizingData();
// Clear the cache - also used to initialize the cache at startup.
void Clear();
......@@ -274,8 +266,6 @@ class CompilationCache {
// Current enable state of the compilation cache.
bool enabled_;
HashMap* eager_optimizing_set_;
friend class Isolate;
DISALLOW_COPY_AND_ASSIGN(CompilationCache);
......
......@@ -109,8 +109,6 @@ void CompilationInfo::DisableOptimization() {
void CompilationInfo::AbortOptimization() {
Handle<Code> code(shared_info()->code());
SetCode(code);
Isolate* isolate = code->GetIsolate();
isolate->compilation_cache()->MarkForLazyOptimizing(closure());
}
......@@ -660,9 +658,6 @@ bool Compiler::CompileLazy(CompilationInfo* info) {
CompilationInfo optimized(function);
optimized.SetOptimizing(AstNode::kNoNumber);
return CompileLazy(&optimized);
} else if (isolate->compilation_cache()->ShouldOptimizeEagerly(
function)) {
isolate->runtime_profiler()->OptimizeSoon(*function);
}
}
}
......
......@@ -1855,11 +1855,6 @@ void Isolate::Exit() {
}
void Isolate::ResetEagerOptimizingData() {
compilation_cache_->ResetEagerOptimizingData();
}
#ifdef DEBUG
#define ISOLATE_FIELD_OFFSET(type, name, ignored) \
const intptr_t Isolate::name##_debug_offset_ = OFFSET_OF(Isolate, name##_);
......
......@@ -978,8 +978,6 @@ class Isolate {
}
#endif
void ResetEagerOptimizingData();
void SetData(void* data) { embedder_data_ = data; }
void* GetData() { return embedder_data_; }
......
......@@ -6287,19 +6287,6 @@ void JSFunction::MarkForLazyRecompilation() {
}
uint32_t JSFunction::SourceHash() {
uint32_t hash = 0;
Object* script = shared()->script();
if (!script->IsUndefined()) {
Object* source = Script::cast(script)->source();
if (source->IsUndefined()) hash = String::cast(source)->Hash();
}
hash ^= ComputeIntegerHash(shared()->start_position_and_type());
hash += ComputeIntegerHash(shared()->end_position());
return hash;
}
bool JSFunction::IsInlineable() {
if (IsBuiltin()) return false;
SharedFunctionInfo* shared_info = shared();
......
......@@ -4924,9 +4924,6 @@ class JSFunction: public JSObject {
// recompilation.
inline bool IsMarkedForLazyRecompilation();
// Compute a hash code for the source code of this function.
uint32_t SourceHash();
// Check whether or not this function is inlineable.
bool IsInlineable();
......
......@@ -43,32 +43,6 @@ namespace v8 {
namespace internal {
class PendingListNode : public Malloced {
public:
explicit PendingListNode(JSFunction* function);
~PendingListNode() { Destroy(); }
PendingListNode* next() const { return next_; }
void set_next(PendingListNode* node) { next_ = node; }
Handle<JSFunction> function() { return Handle<JSFunction>::cast(function_); }
// If the function is garbage collected before we've had the chance
// to optimize it the weak handle will be null.
bool IsValid() { return !function_.is_null(); }
// Returns the number of microseconds this node has been pending.
int Delay() const { return static_cast<int>(OS::Ticks() - start_); }
private:
void Destroy();
static void WeakCallback(v8::Persistent<v8::Value> object, void* data);
PendingListNode* next_;
Handle<Object> function_; // Weak handle.
int64_t start_;
};
// Optimization sampler constants.
static const int kSamplerFrameCount = 2;
static const int kSamplerFrameWeight[kSamplerFrameCount] = { 2, 1 };
......@@ -80,33 +54,10 @@ static const int kSamplerThresholdMin = 1;
static const int kSamplerThresholdDelta = 1;
static const int kSamplerThresholdSizeFactorInit = 3;
static const int kSamplerThresholdSizeFactorMin = 1;
static const int kSamplerThresholdSizeFactorDelta = 1;
static const int kSizeLimit = 1500;
PendingListNode::PendingListNode(JSFunction* function) : next_(NULL) {
GlobalHandles* global_handles = Isolate::Current()->global_handles();
function_ = global_handles->Create(function);
start_ = OS::Ticks();
global_handles->MakeWeak(function_.location(), this, &WeakCallback);
}
void PendingListNode::Destroy() {
if (!IsValid()) return;
GlobalHandles* global_handles = Isolate::Current()->global_handles();
global_handles->Destroy(function_.location());
function_= Handle<Object>::null();
}
void PendingListNode::WeakCallback(v8::Persistent<v8::Value>, void* data) {
reinterpret_cast<PendingListNode*>(data)->Destroy();
}
Atomic32 RuntimeProfiler::state_ = 0;
// TODO(isolates): Create the semaphore lazily and clean it up when no
// longer required.
......@@ -125,16 +76,8 @@ RuntimeProfiler::RuntimeProfiler(Isolate* isolate)
sampler_threshold_(kSamplerThresholdInit),
sampler_threshold_size_factor_(kSamplerThresholdSizeFactorInit),
sampler_ticks_until_threshold_adjustment_(
kSamplerTicksBetweenThresholdAdjustment),
js_ratio_(0),
sampler_window_position_(0),
optimize_soon_list_(NULL),
state_window_position_(0),
state_window_ticks_(0) {
state_counts_[IN_NON_JS_STATE] = kStateWindowSize;
state_counts_[IN_JS_STATE] = 0;
STATIC_ASSERT(IN_NON_JS_STATE == 0);
memset(state_window_, 0, sizeof(state_window_));
kSamplerTicksBetweenThresholdAdjustment),
sampler_window_position_(0) {
ClearSampleBuffer();
}
......@@ -148,16 +91,13 @@ void RuntimeProfiler::GlobalSetup() {
}
void RuntimeProfiler::Optimize(JSFunction* function, bool eager, int delay) {
void RuntimeProfiler::Optimize(JSFunction* function) {
ASSERT(function->IsOptimizable());
if (FLAG_trace_opt) {
PrintF("[marking (%s) ", eager ? "eagerly" : "lazily");
PrintF("[marking ");
function->PrintName();
PrintF(" 0x%" V8PRIxPTR, reinterpret_cast<intptr_t>(function->address()));
PrintF(" for recompilation");
if (delay > 0) {
PrintF(" (delayed %0.3f ms)", static_cast<double>(delay) / 1000);
}
PrintF("]\n");
}
......@@ -243,20 +183,6 @@ void RuntimeProfiler::AddSample(JSFunction* function, int weight) {
void RuntimeProfiler::OptimizeNow() {
HandleScope scope(isolate_);
PendingListNode* current = optimize_soon_list_;
while (current != NULL) {
PendingListNode* next = current->next();
if (current->IsValid()) {
Handle<JSFunction> function = current->function();
int delay = current->Delay();
if (function->IsOptimizable()) {
Optimize(*function, true, delay);
}
}
delete current;
current = next;
}
optimize_soon_list_ = NULL;
// Run through the JavaScript frames and collect them. If we already
// have a sample of the function, we mark it for optimizations
......@@ -303,24 +229,9 @@ void RuntimeProfiler::OptimizeNow() {
: 1;
int threshold = sampler_threshold_ * threshold_size_factor;
int current_js_ratio = NoBarrier_Load(&js_ratio_);
// Adjust threshold depending on the ratio of time spent
// in JS code.
if (current_js_ratio < 20) {
// If we spend less than 20% of the time in JS code,
// do not optimize.
continue;
} else if (current_js_ratio < 75) {
// Below 75% of time spent in JS code, only optimize very
// frequently used functions.
threshold *= 3;
}
if (LookupSample(function) >= threshold) {
Optimize(function, false, 0);
isolate_->compilation_cache()->MarkForEagerOptimizing(
Handle<JSFunction>(function));
Optimize(function);
}
}
......@@ -333,40 +244,8 @@ void RuntimeProfiler::OptimizeNow() {
}
void RuntimeProfiler::OptimizeSoon(JSFunction* function) {
if (!function->IsOptimizable()) return;
PendingListNode* node = new PendingListNode(function);
node->set_next(optimize_soon_list_);
optimize_soon_list_ = node;
}
#ifdef ENABLE_LOGGING_AND_PROFILING
void RuntimeProfiler::UpdateStateRatio(SamplerState current_state) {
SamplerState old_state = state_window_[state_window_position_];
state_counts_[old_state]--;
state_window_[state_window_position_] = current_state;
state_counts_[current_state]++;
ASSERT(IsPowerOf2(kStateWindowSize));
state_window_position_ = (state_window_position_ + 1) &
(kStateWindowSize - 1);
// Note: to calculate correct ratio we have to track how many valid
// ticks are actually in the state window, because on profiler
// startup this number can be less than the window size.
state_window_ticks_ = Min(kStateWindowSize, state_window_ticks_ + 1);
NoBarrier_Store(&js_ratio_, state_counts_[IN_JS_STATE] * 100 /
state_window_ticks_);
}
#endif
void RuntimeProfiler::NotifyTick() {
#ifdef ENABLE_LOGGING_AND_PROFILING
// Record state sample.
SamplerState state = IsSomeIsolateInJS()
? IN_JS_STATE
: IN_NON_JS_STATE;
UpdateStateRatio(state);
isolate_->stack_guard()->RequestRuntimeProfilerTick();
#endif
}
......@@ -424,7 +303,6 @@ void RuntimeProfiler::HandleWakeUp(Isolate* isolate) {
// to get the right count of active isolates.
NoBarrier_AtomicIncrement(&state_, 1);
semaphore_->Signal();
isolate->ResetEagerOptimizingData();
#endif
}
......@@ -471,15 +349,8 @@ void RuntimeProfiler::UpdateSamplesAfterCompact(ObjectVisitor* visitor) {
bool RuntimeProfilerRateLimiter::SuspendIfNecessary() {
#ifdef ENABLE_LOGGING_AND_PROFILING
static const int kNonJSTicksThreshold = 100;
if (RuntimeProfiler::IsSomeIsolateInJS()) {
non_js_ticks_ = 0;
} else {
if (non_js_ticks_ < kNonJSTicksThreshold) {
++non_js_ticks_;
} else {
return RuntimeProfiler::WaitForSomeIsolateToEnterJS();
}
if (!RuntimeProfiler::IsSomeIsolateInJS()) {
return RuntimeProfiler::WaitForSomeIsolateToEnterJS();
}
#endif
return false;
......
......@@ -37,7 +37,6 @@ namespace internal {
class Isolate;
class JSFunction;
class Object;
class PendingListNode;
class Semaphore;
class RuntimeProfiler {
......@@ -52,7 +51,6 @@ class RuntimeProfiler {
}
void OptimizeNow();
void OptimizeSoon(JSFunction* function);
void NotifyTick();
......@@ -106,7 +104,7 @@ class RuntimeProfiler {
static void HandleWakeUp(Isolate* isolate);
void Optimize(JSFunction* function, bool eager, int delay);
void Optimize(JSFunction* function);
void AttemptOnStackReplacement(JSFunction* function);
......@@ -118,31 +116,16 @@ class RuntimeProfiler {
void AddSample(JSFunction* function, int weight);
#ifdef ENABLE_LOGGING_AND_PROFILING
void UpdateStateRatio(SamplerState current_state);
#endif
Isolate* isolate_;
int sampler_threshold_;
int sampler_threshold_size_factor_;
int sampler_ticks_until_threshold_adjustment_;
// The ratio of ticks spent in JS code in percent.
Atomic32 js_ratio_;
Object* sampler_window_[kSamplerWindowSize];
int sampler_window_position_;
int sampler_window_weight_[kSamplerWindowSize];
// Support for pending 'optimize soon' requests.
PendingListNode* optimize_soon_list_;
SamplerState state_window_[kStateWindowSize];
int state_window_position_;
int state_window_ticks_;
int state_counts_[2];
// Possible state values:
// -1 => the profiler thread is waiting on the semaphore
// 0 or positive => the number of isolates running JavaScript code.
......@@ -159,7 +142,7 @@ class RuntimeProfiler {
// Rate limiter intended to be used in the profiler thread.
class RuntimeProfilerRateLimiter BASE_EMBEDDED {
public:
RuntimeProfilerRateLimiter() : non_js_ticks_(0) { }
RuntimeProfilerRateLimiter() {}
// Suspends the current thread (which must be the profiler thread)
// when not executing JavaScript to minimize CPU usage. Returns
......@@ -170,8 +153,6 @@ class RuntimeProfilerRateLimiter BASE_EMBEDDED {
bool SuspendIfNecessary();
private:
int non_js_ticks_;
DISALLOW_COPY_AND_ASSIGN(RuntimeProfilerRateLimiter);
};
......
......@@ -7821,7 +7821,6 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_NotifyDeoptimized) {
}
}
isolate->compilation_cache()->MarkForLazyOptimizing(function);
if (type == Deoptimizer::EAGER) {
RUNTIME_ASSERT(function->IsOptimized());
} else {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment