Count-based profiling for primitive functions (hidden behind a flag)

Review URL: https://chromiumcodereview.appspot.com/9361026

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@10657 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
parent fdf31d1b
// Copyright 2011 the V8 project authors. All rights reserved.
// Copyright 2012 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
......@@ -139,6 +139,27 @@ void FullCodeGenerator::Generate(CompilationInfo* info) {
}
#endif
// We can optionally optimize based on counters rather than statistical
// sampling.
if (info->ShouldSelfOptimize()) {
if (FLAG_trace_opt) {
PrintF("[adding self-optimization header to %s]\n",
*info->function()->debug_name()->ToCString());
}
MaybeObject* maybe_cell = isolate()->heap()->AllocateJSGlobalPropertyCell(
Smi::FromInt(Compiler::kCallsUntilPrimitiveOpt));
JSGlobalPropertyCell* cell;
if (maybe_cell->To(&cell)) {
__ mov(r2, Operand(Handle<JSGlobalPropertyCell>(cell)));
__ ldr(r3, FieldMemOperand(r2, JSGlobalPropertyCell::kValueOffset));
__ sub(r3, r3, Operand(Smi::FromInt(1)), SetCC);
__ str(r3, FieldMemOperand(r2, JSGlobalPropertyCell::kValueOffset));
Handle<Code> compile_stub(
isolate()->builtins()->builtin(Builtins::kLazyRecompile));
__ Jump(compile_stub, RelocInfo::CODE_TARGET, eq);
}
}
// Strict mode functions and builtins need to replace the receiver
// with undefined when called as functions (without an explicit
// receiver object). r5 is zero for method calls and non-zero for
......
......@@ -168,11 +168,6 @@ LanguageMode FunctionLiteral::language_mode() const {
}
bool FunctionLiteral::ShouldSelfOptimize() {
return !flags()->Contains(kDontSelfOptimize);
}
ObjectLiteral::Property::Property(Literal* key, Expression* value) {
emit_store_ = true;
key_ = key;
......
......@@ -1817,8 +1817,6 @@ class FunctionLiteral: public Expression {
return HasDuplicateParameters::decode(bitfield_);
}
bool ShouldSelfOptimize();
int ast_node_count() { return ast_properties_.node_count(); }
AstProperties::Flags* flags() { return ast_properties_.flags(); }
void set_ast_properties(AstProperties* ast_properties) {
......
......@@ -110,6 +110,18 @@ void CompilationInfo::DisableOptimization() {
}
// Primitive functions are unlikely to be picked up by the stack-walking
// profiler, so they trigger their own optimization when they're called
// for the SharedFunctionInfo::kCallsUntilPrimitiveOptimization-th time.
bool CompilationInfo::ShouldSelfOptimize() {
return FLAG_counting_profiler &&
FLAG_crankshaft &&
!Serializer::enabled() &&
!function()->flags()->Contains(kDontSelfOptimize) &&
(shared_info().is_null() || !shared_info()->optimization_disabled());
}
void CompilationInfo::AbortOptimization() {
Handle<Code> code(shared_info()->code());
SetCode(code);
......@@ -654,6 +666,7 @@ bool Compiler::CompileLazy(CompilationInfo* info) {
shared->set_code_age(0);
shared->set_dont_crankshaft(lit->flags()->Contains(kDontOptimize));
shared->set_dont_inline(lit->flags()->Contains(kDontInline));
shared->set_ast_node_count(lit->ast_node_count());
if (info->AllowOptimize() && !shared->optimization_disabled()) {
// If we're asked to always optimize, we compile the optimized
......
......@@ -168,6 +168,9 @@ class CompilationInfo BASE_EMBEDDED {
return V8::UseCrankshaft() && !closure_.is_null();
}
// Determines whether or not to insert a self-optimization header.
bool ShouldSelfOptimize();
// Disable all optimization attempts of this info for the rest of the
// current compilation pipeline.
void AbortOptimization();
......@@ -280,6 +283,9 @@ class Compiler : public AllStatic {
static const int kMaxInliningLevels = 3;
// Call count before primitive functions trigger their own optimization.
static const int kCallsUntilPrimitiveOpt = 200;
// All routines return a SharedFunctionInfo.
// If an error occurs an exception is raised and the return handle
// contains NULL.
......
......@@ -165,6 +165,9 @@ DEFINE_int(stress_runs, 0, "number of stress runs")
DEFINE_bool(optimize_closures, true, "optimize closures")
DEFINE_int(loop_weight, 1, "loop weight for representation inference")
// Count-based optimization decisions.
DEFINE_bool(counting_profiler, false, "use experimental counter-based profiler")
// assembler-ia32.cc / assembler-arm.cc / assembler-x64.cc
DEFINE_bool(debug_code, false,
"generate extra code (assertions) for debugging")
......
......@@ -297,6 +297,9 @@ bool FullCodeGenerator::MakeCode(CompilationInfo* info) {
code->set_stack_check_table_offset(table_offset);
CodeGenerator::PrintCode(code, info);
info->SetCode(code); // May be an empty handle.
if (!code.is_null()) {
isolate->runtime_profiler()->NotifyCodeGenerated(code->instruction_size());
}
#ifdef ENABLE_GDB_JIT_INTERFACE
if (FLAG_gdbjit && !code.is_null()) {
GDBJITLineInfo* lineinfo =
......
......@@ -1201,7 +1201,9 @@ void Heap::Scavenge() {
promotion_queue_.Destroy();
LiveObjectList::UpdateReferencesForScavengeGC();
isolate()->runtime_profiler()->UpdateSamplesAfterScavenge();
if (!FLAG_counting_profiler) {
isolate()->runtime_profiler()->UpdateSamplesAfterScavenge();
}
incremental_marking()->UpdateMarkingDequeAfterScavenge();
ASSERT(new_space_front == new_space_.top());
......@@ -2866,6 +2868,7 @@ MaybeObject* Heap::AllocateSharedFunctionInfo(Object* name) {
share->set_initial_map(undefined_value(), SKIP_WRITE_BARRIER);
share->set_this_property_assignments(undefined_value(), SKIP_WRITE_BARRIER);
share->set_deopt_counter(FLAG_deopt_every_n_times);
share->set_profiler_ticks(0);
share->set_ast_node_count(0);
// Set integer fields (smi or int, depending on the architecture).
......
// Copyright 2011 the V8 project authors. All rights reserved.
// Copyright 2012 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
......@@ -129,6 +129,26 @@ void FullCodeGenerator::Generate(CompilationInfo* info) {
}
#endif
// We can optionally optimize based on counters rather than statistical
// sampling.
if (info->ShouldSelfOptimize()) {
if (FLAG_trace_opt) {
PrintF("[adding self-optimization header to %s]\n",
*info->function()->debug_name()->ToCString());
}
MaybeObject* maybe_cell = isolate()->heap()->AllocateJSGlobalPropertyCell(
Smi::FromInt(Compiler::kCallsUntilPrimitiveOpt));
JSGlobalPropertyCell* cell;
if (maybe_cell->To(&cell)) {
__ sub(Operand::Cell(Handle<JSGlobalPropertyCell>(cell)),
Immediate(Smi::FromInt(1)));
Handle<Code> compile_stub(
isolate()->builtins()->builtin(Builtins::kLazyRecompile));
STATIC_ASSERT(kSmiTag == 0);
__ j(zero, compile_stub);
}
}
// Strict mode functions and builtins need to replace the receiver
// with undefined when called as functions (without an explicit
// receiver object). ecx is zero for method calls and non-zero for
......
// Copyright 2011 the V8 project authors. All rights reserved.
// Copyright 2012 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
......@@ -29,6 +29,8 @@
#define V8_IC_INL_H_
#include "ic.h"
#include "compiler.h"
#include "debug.h"
#include "macro-assembler.h"
......@@ -89,6 +91,7 @@ void IC::SetTargetAtAddress(Address address, Code* target) {
Assembler::set_target_address_at(address, target->instruction_start());
target->GetHeap()->incremental_marking()->RecordCodeTargetPatch(address,
target);
PostPatching();
}
......
......@@ -292,6 +292,31 @@ Failure* IC::ReferenceError(const char* type, Handle<String> name) {
}
void IC::PostPatching() {
if (FLAG_counting_profiler) {
Isolate::Current()->runtime_profiler()->NotifyICChanged();
// We do not want to optimize until the ICs have settled down,
// so when they are patched, we postpone optimization for the
// current function and the functions above it on the stack that
// might want to inline this one.
StackFrameIterator it;
if (it.done()) return;
it.Advance();
static const int kStackFramesToMark = Compiler::kMaxInliningLevels - 1;
for (int i = 0; i < kStackFramesToMark; ++i) {
if (it.done()) return;
StackFrame* raw_frame = it.frame();
if (raw_frame->is_java_script()) {
JSFunction* function =
JSFunction::cast(JavaScriptFrame::cast(raw_frame)->function());
function->shared()->set_profiler_ticks(0);
}
it.Advance();
}
}
}
void IC::Clear(Address address) {
Code* target = GetTargetAtAddress(address);
......
......@@ -165,6 +165,7 @@ class IC {
// Access the target code for the given IC address.
static inline Code* GetTargetAtAddress(Address address);
static inline void SetTargetAtAddress(Address address, Code* target);
static void PostPatching();
private:
// Frame pointer for the frame that uses (calls) the IC.
......
......@@ -2373,8 +2373,10 @@ void MarkCompactCollector::AfterMarking() {
code_flusher_->ProcessCandidates();
}
// Clean up dead objects from the runtime profiler.
heap()->isolate()->runtime_profiler()->RemoveDeadSamples();
if (!FLAG_counting_profiler) {
// Clean up dead objects from the runtime profiler.
heap()->isolate()->runtime_profiler()->RemoveDeadSamples();
}
}
......@@ -3381,9 +3383,11 @@ void MarkCompactCollector::EvacuateNewSpaceAndCandidates() {
heap_->UpdateReferencesInExternalStringTable(
&UpdateReferenceInExternalStringTableEntry);
// Update JSFunction pointers from the runtime profiler.
heap()->isolate()->runtime_profiler()->UpdateSamplesAfterCompact(
&updating_visitor);
if (!FLAG_counting_profiler) {
// Update JSFunction pointers from the runtime profiler.
heap()->isolate()->runtime_profiler()->UpdateSamplesAfterCompact(
&updating_visitor);
}
EvacuationWeakObjectRetainer evacuation_object_retainer;
heap()->ProcessWeakReferences(&evacuation_object_retainer);
......
......@@ -3530,6 +3530,8 @@ ACCESSORS(SharedFunctionInfo, inferred_name, String, kInferredNameOffset)
ACCESSORS(SharedFunctionInfo, this_property_assignments, Object,
kThisPropertyAssignmentsOffset)
SMI_ACCESSORS(SharedFunctionInfo, profiler_ticks, kProfilerTicksOffset)
BOOL_ACCESSORS(FunctionTemplateInfo, flag, hidden_prototype,
kHiddenPrototypeBit)
BOOL_ACCESSORS(FunctionTemplateInfo, flag, undetectable, kUndetectableBit)
......
......@@ -5200,6 +5200,9 @@ class SharedFunctionInfo: public HeapObject {
inline int deopt_counter();
inline void set_deopt_counter(int counter);
inline int profiler_ticks();
inline void set_profiler_ticks(int ticks);
inline int ast_node_count();
inline void set_ast_node_count(int count);
......@@ -5375,10 +5378,12 @@ class SharedFunctionInfo: public HeapObject {
kInferredNameOffset + kPointerSize;
static const int kThisPropertyAssignmentsOffset =
kInitialMapOffset + kPointerSize;
static const int kProfilerTicksOffset =
kThisPropertyAssignmentsOffset + kPointerSize;
#if V8_HOST_ARCH_32_BIT
// Smi fields.
static const int kLengthOffset =
kThisPropertyAssignmentsOffset + kPointerSize;
kProfilerTicksOffset + kPointerSize;
static const int kFormalParameterCountOffset = kLengthOffset + kPointerSize;
static const int kExpectedNofPropertiesOffset =
kFormalParameterCountOffset + kPointerSize;
......@@ -5412,7 +5417,7 @@ class SharedFunctionInfo: public HeapObject {
// word is not set and thus this word cannot be treated as pointer
// to HeapObject during old space traversal.
static const int kLengthOffset =
kThisPropertyAssignmentsOffset + kPointerSize;
kProfilerTicksOffset + kPointerSize;
static const int kFormalParameterCountOffset =
kLengthOffset + kIntSize;
......
// Copyright 2011 the V8 project authors. All rights reserved.
// Copyright 2012 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
......@@ -46,6 +46,8 @@ namespace internal {
// Optimization sampler constants.
static const int kSamplerFrameCount = 2;
// Constants for statistical profiler.
static const int kSamplerFrameWeight[kSamplerFrameCount] = { 2, 1 };
static const int kSamplerTicksBetweenThresholdAdjustment = 32;
......@@ -58,6 +60,16 @@ static const int kSamplerThresholdSizeFactorInit = 3;
static const int kSizeLimit = 1500;
// Constants for counter based profiler.
// Number of times a function has to be seen on the stack before it is
// optimized.
static const int kProfilerTicksBeforeOptimization = 2;
// Maximum size in bytes of generated code for a function to be optimized
// the very first time it is seen on the stack.
static const int kMaxSizeEarlyOpt = 500;
Atomic32 RuntimeProfiler::state_ = 0;
// TODO(isolates): Create the semaphore lazily and clean it up when no
......@@ -90,13 +102,13 @@ void RuntimeProfiler::GlobalSetup() {
}
void RuntimeProfiler::Optimize(JSFunction* function) {
void RuntimeProfiler::Optimize(JSFunction* function, const char* reason) {
ASSERT(function->IsOptimizable());
if (FLAG_trace_opt) {
PrintF("[marking ");
function->PrintName();
PrintF(" 0x%" V8PRIxPTR, reinterpret_cast<intptr_t>(function->address()));
PrintF(" for recompilation");
PrintF(" for recompilation, reason: %s", reason);
PrintF("]\n");
}
......@@ -192,17 +204,19 @@ void RuntimeProfiler::OptimizeNow() {
JavaScriptFrame* frame = it.frame();
JSFunction* function = JSFunction::cast(frame->function());
// Adjust threshold each time we have processed
// a certain number of ticks.
if (sampler_ticks_until_threshold_adjustment_ > 0) {
sampler_ticks_until_threshold_adjustment_--;
if (sampler_ticks_until_threshold_adjustment_ <= 0) {
// If the threshold is not already at the minimum
// modify and reset the ticks until next adjustment.
if (sampler_threshold_ > kSamplerThresholdMin) {
sampler_threshold_ -= kSamplerThresholdDelta;
sampler_ticks_until_threshold_adjustment_ =
kSamplerTicksBetweenThresholdAdjustment;
if (!FLAG_counting_profiler) {
// Adjust threshold each time we have processed
// a certain number of ticks.
if (sampler_ticks_until_threshold_adjustment_ > 0) {
sampler_ticks_until_threshold_adjustment_--;
if (sampler_ticks_until_threshold_adjustment_ <= 0) {
// If the threshold is not already at the minimum
// modify and reset the ticks until next adjustment.
if (sampler_threshold_ > kSamplerThresholdMin) {
sampler_threshold_ -= kSamplerThresholdDelta;
sampler_ticks_until_threshold_adjustment_ =
kSamplerTicksBetweenThresholdAdjustment;
}
}
}
}
......@@ -217,25 +231,55 @@ void RuntimeProfiler::OptimizeNow() {
// Do not record non-optimizable functions.
if (!function->IsOptimizable()) continue;
samples[sample_count++] = function;
int function_size = function->shared()->SourceSize();
int threshold_size_factor = (function_size > kSizeLimit)
? sampler_threshold_size_factor_
: 1;
if (FLAG_counting_profiler) {
int ticks = function->shared()->profiler_ticks();
if (ticks >= kProfilerTicksBeforeOptimization) {
// If this particular function hasn't had any ICs patched for enough
// ticks, optimize it now.
Optimize(function, "hot and stable");
} else if (!any_ic_changed_ &&
function->shared()->code()->instruction_size() < kMaxSizeEarlyOpt) {
// If no IC was patched since the last tick and this function is very
// small, optimistically optimize it now.
Optimize(function, "small function");
} else if (!code_generated_ &&
!any_ic_changed_ &&
total_code_generated_ > 0 &&
total_code_generated_ < 2000) {
// If no code was generated and no IC was patched since the last tick,
// but a little code has already been generated since last Reset(),
// then type info might already be stable and we can optimize now.
Optimize(function, "stable on startup");
} else {
function->shared()->set_profiler_ticks(ticks + 1);
}
} else { // !FLAG_counting_profiler
samples[sample_count++] = function;
int function_size = function->shared()->SourceSize();
int threshold_size_factor = (function_size > kSizeLimit)
? sampler_threshold_size_factor_
: 1;
int threshold = sampler_threshold_ * threshold_size_factor;
int threshold = sampler_threshold_ * threshold_size_factor;
if (LookupSample(function) >= threshold) {
Optimize(function);
if (LookupSample(function) >= threshold) {
Optimize(function, "sampler window lookup");
}
}
}
// Add the collected functions as samples. It's important not to do
// this as part of collecting them because this will interfere with
// the sample lookup in case of recursive functions.
for (int i = 0; i < sample_count; i++) {
AddSample(samples[i], kSamplerFrameWeight[i]);
if (FLAG_counting_profiler) {
any_ic_changed_ = false;
code_generated_ = false;
} else { // !FLAG_counting_profiler
// Add the collected functions as samples. It's important not to do
// this as part of collecting them because this will interfere with
// the sample lookup in case of recursive functions.
for (int i = 0; i < sample_count; i++) {
AddSample(samples[i], kSamplerFrameWeight[i]);
}
}
}
......@@ -247,7 +291,9 @@ void RuntimeProfiler::NotifyTick() {
void RuntimeProfiler::SetUp() {
ASSERT(has_been_globally_set_up_);
ClearSampleBuffer();
if (!FLAG_counting_profiler) {
ClearSampleBuffer();
}
// If the ticker hasn't already started, make sure to do so to get
// the ticks for the runtime profiler.
if (IsEnabled()) isolate_->logger()->EnsureTickerStarted();
......@@ -255,10 +301,14 @@ void RuntimeProfiler::SetUp() {
void RuntimeProfiler::Reset() {
sampler_threshold_ = kSamplerThresholdInit;
sampler_threshold_size_factor_ = kSamplerThresholdSizeFactorInit;
sampler_ticks_until_threshold_adjustment_ =
kSamplerTicksBetweenThresholdAdjustment;
if (FLAG_counting_profiler) {
total_code_generated_ = 0;
} else { // !FLAG_counting_profiler
sampler_threshold_ = kSamplerThresholdInit;
sampler_threshold_size_factor_ = kSamplerThresholdSizeFactorInit;
sampler_ticks_until_threshold_adjustment_ =
kSamplerTicksBetweenThresholdAdjustment;
}
}
......
// Copyright 2010 the V8 project authors. All rights reserved.
// Copyright 2012 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
......@@ -61,6 +61,15 @@ class RuntimeProfiler {
Object** SamplerWindowAddress();
int SamplerWindowSize();
void NotifyICChanged() { any_ic_changed_ = true; }
void NotifyCodeGenerated(int generated_code_size) {
if (FLAG_counting_profiler) {
code_generated_ = true;
total_code_generated_ += generated_code_size;
}
}
// Rate limiting support.
// VM thread interface.
......@@ -97,7 +106,7 @@ class RuntimeProfiler {
static void HandleWakeUp(Isolate* isolate);
void Optimize(JSFunction* function);
void Optimize(JSFunction* function, const char* reason);
void AttemptOnStackReplacement(JSFunction* function);
......@@ -119,6 +128,10 @@ class RuntimeProfiler {
int sampler_window_position_;
int sampler_window_weight_[kSamplerWindowSize];
bool any_ic_changed_;
bool code_generated_;
int total_code_generated_;
// Possible state values:
// -1 => the profiler thread is waiting on the semaphore
// 0 or positive => the number of isolates running JavaScript code.
......
......@@ -8427,6 +8427,8 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_LazyRecompile) {
ASSERT(args.length() == 1);
Handle<JSFunction> function = args.at<JSFunction>(0);
function->shared()->set_profiler_ticks(0);
// If the function is not compiled ignore the lazy
// recompilation. This can happen if the debugger is activated and
// the function is returned to the not compiled state.
......
// Copyright 2011 the V8 project authors. All rights reserved.
// Copyright 2012 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
......@@ -129,6 +129,27 @@ void FullCodeGenerator::Generate(CompilationInfo* info) {
}
#endif
// We can optionally optimize based on counters rather than statistical
// sampling.
if (info->ShouldSelfOptimize()) {
if (FLAG_trace_opt) {
PrintF("[adding self-optimization header to %s]\n",
*info->function()->debug_name()->ToCString());
}
MaybeObject* maybe_cell = isolate()->heap()->AllocateJSGlobalPropertyCell(
Smi::FromInt(Compiler::kCallsUntilPrimitiveOpt));
JSGlobalPropertyCell* cell;
if (maybe_cell->To(&cell)) {
__ movq(rax, Handle<JSGlobalPropertyCell>(cell),
RelocInfo::EMBEDDED_OBJECT);
__ SmiAddConstant(FieldOperand(rax, JSGlobalPropertyCell::kValueOffset),
Smi::FromInt(-1));
Handle<Code> compile_stub(
isolate()->builtins()->builtin(Builtins::kLazyRecompile));
__ j(zero, compile_stub, RelocInfo::CODE_TARGET);
}
}
// Strict mode functions and builtins need to replace the receiver
// with undefined when called as functions (without an explicit
// receiver object). rcx is zero for method calls and non-zero for
......
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