Commit 7f7ba49f authored by fpizlo@apple.com's avatar fpizlo@apple.com

JSC should scale the optimization threshold for a code block according to the cost of compiling it

https://bugs.webkit.org/show_bug.cgi?id=104406

Reviewed by Oliver Hunt.

We've long known that we want to scale the execution count threshold needed for the DFG
to kick in to scale according to some estimate of the cost of compiling that code block.
This institutes a relationship like this:
        
threshold = thresholdSetting * (a * sqrt(instructionCount + b) + abs(c * instructionCount) + d
        
Where a, b, c, d are coefficients derived from fitting the above expression to various
data points, which I chose based on looking at one benchmark (3d-cube) and from my
own intuitions.
        
Making this work well also required changing the thresholdForOptimizeAfterLongWarmUp
from 5000 to 1000.
        
This is a >1% speed-up on SunSpider, a >3% speed-up on V8Spider, ~1% speed-up on V8v7,
neutral on Octane, and neutral on Kraken.
        
I also out-of-lined a bunch of methods related to these heuristics, because I couldn't
stand having them defined in the header anymore. I also made improvements to debugging
code because I needed it for tuning this change.

* CMakeLists.txt:
* GNUmakefile.list.am:
* JavaScriptCore.vcproj/JavaScriptCore/JavaScriptCore.vcproj:
* JavaScriptCore.xcodeproj/project.pbxproj:
* Target.pri:
* bytecode/CodeBlock.cpp:
(JSC::CodeBlock::sourceCodeForTools):
(JSC::CodeBlock::sourceCodeOnOneLine):
(JSC::CodeBlock::dumpBytecode):
(JSC::CodeBlock::CodeBlock):
(JSC::CodeBlock::reoptimizationRetryCounter):
(JSC::CodeBlock::countReoptimization):
(JSC::CodeBlock::optimizationThresholdScalingFactor):
(JSC::clipThreshold):
(JSC::CodeBlock::counterValueForOptimizeAfterWarmUp):
(JSC::CodeBlock::counterValueForOptimizeAfterLongWarmUp):
(JSC::CodeBlock::counterValueForOptimizeSoon):
(JSC::CodeBlock::checkIfOptimizationThresholdReached):
(JSC::CodeBlock::optimizeNextInvocation):
(JSC::CodeBlock::dontOptimizeAnytimeSoon):
(JSC::CodeBlock::optimizeAfterWarmUp):
(JSC::CodeBlock::optimizeAfterLongWarmUp):
(JSC::CodeBlock::optimizeSoon):
(JSC::CodeBlock::adjustedExitCountThreshold):
(JSC::CodeBlock::exitCountThresholdForReoptimization):
(JSC::CodeBlock::exitCountThresholdForReoptimizationFromLoop):
(JSC::CodeBlock::shouldReoptimizeNow):
(JSC::CodeBlock::shouldReoptimizeFromLoopNow):
* bytecode/CodeBlock.h:
* bytecode/ExecutionCounter.cpp:
(JSC::ExecutionCounter::hasCrossedThreshold):
* bytecode/ReduceWhitespace.cpp: Added.
(JSC::reduceWhitespace):
* bytecode/ReduceWhitespace.h: Added.
* dfg/DFGCapabilities.cpp:
(JSC::DFG::mightCompileEval):
(JSC::DFG::mightCompileProgram):
(JSC::DFG::mightCompileFunctionForCall):
(JSC::DFG::mightCompileFunctionForConstruct):
(JSC::DFG::mightInlineFunctionForCall):
(JSC::DFG::mightInlineFunctionForConstruct):
* dfg/DFGCapabilities.h:
* dfg/DFGDisassembler.cpp:
(JSC::DFG::Disassembler::dumpHeader):
* dfg/DFGOSREntry.cpp:
(JSC::DFG::prepareOSREntry):
* jit/JITDisassembler.cpp:
(JSC::JITDisassembler::dumpHeader):
* jit/JITStubs.cpp:
(JSC::DEFINE_STUB_FUNCTION):
* llint/LLIntSlowPaths.cpp:
(JSC::LLInt::entryOSR):
(JSC::LLInt::LLINT_SLOW_PATH_DECL):
* profiler/ProfilerDatabase.cpp:
(JSC::Profiler::Database::ensureBytecodesFor):
* runtime/Options.h:



git-svn-id: http://svn.webkit.org/repository/webkit/trunk@137094 268f45cc-cd09-0410-ab3c-d52691b4dbfc
parent ecc4335f
......@@ -58,6 +58,7 @@ set(JavaScriptCore_SOURCES
bytecode/PolymorphicPutByIdList.cpp
bytecode/SpeculatedType.cpp
bytecode/PutByIdStatus.cpp
bytecode/ReduceWhitespace.cpp
bytecode/ResolveGlobalStatus.cpp
bytecode/SamplingTool.cpp
bytecode/SpecialPointer.cpp
......
2012-12-08 Filip Pizlo <fpizlo@apple.com>
JSC should scale the optimization threshold for a code block according to the cost of compiling it
https://bugs.webkit.org/show_bug.cgi?id=104406
Reviewed by Oliver Hunt.
We've long known that we want to scale the execution count threshold needed for the DFG
to kick in to scale according to some estimate of the cost of compiling that code block.
This institutes a relationship like this:
threshold = thresholdSetting * (a * sqrt(instructionCount + b) + abs(c * instructionCount) + d
Where a, b, c, d are coefficients derived from fitting the above expression to various
data points, which I chose based on looking at one benchmark (3d-cube) and from my
own intuitions.
Making this work well also required changing the thresholdForOptimizeAfterLongWarmUp
from 5000 to 1000.
This is a >1% speed-up on SunSpider, a >3% speed-up on V8Spider, ~1% speed-up on V8v7,
neutral on Octane, and neutral on Kraken.
I also out-of-lined a bunch of methods related to these heuristics, because I couldn't
stand having them defined in the header anymore. I also made improvements to debugging
code because I needed it for tuning this change.
* CMakeLists.txt:
* GNUmakefile.list.am:
* JavaScriptCore.vcproj/JavaScriptCore/JavaScriptCore.vcproj:
* JavaScriptCore.xcodeproj/project.pbxproj:
* Target.pri:
* bytecode/CodeBlock.cpp:
(JSC::CodeBlock::sourceCodeForTools):
(JSC::CodeBlock::sourceCodeOnOneLine):
(JSC::CodeBlock::dumpBytecode):
(JSC::CodeBlock::CodeBlock):
(JSC::CodeBlock::reoptimizationRetryCounter):
(JSC::CodeBlock::countReoptimization):
(JSC::CodeBlock::optimizationThresholdScalingFactor):
(JSC::clipThreshold):
(JSC::CodeBlock::counterValueForOptimizeAfterWarmUp):
(JSC::CodeBlock::counterValueForOptimizeAfterLongWarmUp):
(JSC::CodeBlock::counterValueForOptimizeSoon):
(JSC::CodeBlock::checkIfOptimizationThresholdReached):
(JSC::CodeBlock::optimizeNextInvocation):
(JSC::CodeBlock::dontOptimizeAnytimeSoon):
(JSC::CodeBlock::optimizeAfterWarmUp):
(JSC::CodeBlock::optimizeAfterLongWarmUp):
(JSC::CodeBlock::optimizeSoon):
(JSC::CodeBlock::adjustedExitCountThreshold):
(JSC::CodeBlock::exitCountThresholdForReoptimization):
(JSC::CodeBlock::exitCountThresholdForReoptimizationFromLoop):
(JSC::CodeBlock::shouldReoptimizeNow):
(JSC::CodeBlock::shouldReoptimizeFromLoopNow):
* bytecode/CodeBlock.h:
* bytecode/ExecutionCounter.cpp:
(JSC::ExecutionCounter::hasCrossedThreshold):
* bytecode/ReduceWhitespace.cpp: Added.
(JSC::reduceWhitespace):
* bytecode/ReduceWhitespace.h: Added.
* dfg/DFGCapabilities.cpp:
(JSC::DFG::mightCompileEval):
(JSC::DFG::mightCompileProgram):
(JSC::DFG::mightCompileFunctionForCall):
(JSC::DFG::mightCompileFunctionForConstruct):
(JSC::DFG::mightInlineFunctionForCall):
(JSC::DFG::mightInlineFunctionForConstruct):
* dfg/DFGCapabilities.h:
* dfg/DFGDisassembler.cpp:
(JSC::DFG::Disassembler::dumpHeader):
* dfg/DFGOSREntry.cpp:
(JSC::DFG::prepareOSREntry):
* jit/JITDisassembler.cpp:
(JSC::JITDisassembler::dumpHeader):
* jit/JITStubs.cpp:
(JSC::DEFINE_STUB_FUNCTION):
* llint/LLIntSlowPaths.cpp:
(JSC::LLInt::entryOSR):
(JSC::LLInt::LLINT_SLOW_PATH_DECL):
* profiler/ProfilerDatabase.cpp:
(JSC::Profiler::Database::ensureBytecodesFor):
* runtime/Options.h:
2012-12-07 Jonathan Liu <net147@gmail.com>
Add missing forward declaration for JSC::ArrayAllocationProfile
......
......@@ -133,6 +133,8 @@ javascriptcore_sources += \
Source/JavaScriptCore/bytecode/PutByIdStatus.cpp \
Source/JavaScriptCore/bytecode/PutByIdStatus.h \
Source/JavaScriptCore/bytecode/PutKind.h \
Source/JavaScriptCore/bytecode/ReduceWhitespace.cpp \
Source/JavaScriptCore/bytecode/ReduceWhitespace.h \
Source/JavaScriptCore/bytecode/ResolveGlobalStatus.cpp \
Source/JavaScriptCore/bytecode/ResolveGlobalStatus.h \
Source/JavaScriptCore/bytecode/ResolveOperation.h \
......
......@@ -1809,6 +1809,14 @@
RelativePath="..\..\bytecode\PolymorphicPutByIdList.cpp"
>
</File>
<File
RelativePath="..\..\bytecode\ReduceWhitespace.cpp"
>
</File>
<File
RelativePath="..\..\bytecode\ReduceWhitespace.h"
>
</File>
<File
RelativePath="..\..\bytecode\SpecialPointer.cpp"
>
......
......@@ -270,6 +270,8 @@
0FF427651591A1CE004CB9FF /* DFGDisassembler.h in Headers */ = {isa = PBXBuildFile; fileRef = 0FF427621591A1C9004CB9FF /* DFGDisassembler.h */; settings = {ATTRIBUTES = (Private, ); }; };
0FF42771159275D5004CB9FF /* ResolveGlobalStatus.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0FF4276E159275D2004CB9FF /* ResolveGlobalStatus.cpp */; };
0FF42772159275D8004CB9FF /* ResolveGlobalStatus.h in Headers */ = {isa = PBXBuildFile; fileRef = 0FF4276F159275D2004CB9FF /* ResolveGlobalStatus.h */; settings = {ATTRIBUTES = (Private, ); }; };
0FF60AC216740F8300029779 /* ReduceWhitespace.h in Headers */ = {isa = PBXBuildFile; fileRef = 0FF60AC016740F8100029779 /* ReduceWhitespace.h */; settings = {ATTRIBUTES = (Private, ); }; };
0FF60AC316740F8800029779 /* ReduceWhitespace.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0FF60ABF16740F8100029779 /* ReduceWhitespace.cpp */; };
0FF7168C15A3B235008F5DAA /* PropertyOffset.h in Headers */ = {isa = PBXBuildFile; fileRef = 0FF7168A15A3B231008F5DAA /* PropertyOffset.h */; settings = {ATTRIBUTES = (Private, ); }; };
0FF729A5166AD351000F5BA3 /* ProfilerBytecode.h in Headers */ = {isa = PBXBuildFile; fileRef = 0FF72993166AD347000F5BA3 /* ProfilerBytecode.h */; settings = {ATTRIBUTES = (Private, ); }; };
0FF729AD166AD35C000F5BA3 /* ProfilerBytecode.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0FF72992166AD347000F5BA3 /* ProfilerBytecode.cpp */; };
......@@ -1095,6 +1097,8 @@
0FF427621591A1C9004CB9FF /* DFGDisassembler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = DFGDisassembler.h; path = dfg/DFGDisassembler.h; sourceTree = "<group>"; };
0FF4276E159275D2004CB9FF /* ResolveGlobalStatus.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ResolveGlobalStatus.cpp; sourceTree = "<group>"; };
0FF4276F159275D2004CB9FF /* ResolveGlobalStatus.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ResolveGlobalStatus.h; sourceTree = "<group>"; };
0FF60ABF16740F8100029779 /* ReduceWhitespace.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ReduceWhitespace.cpp; sourceTree = "<group>"; };
0FF60AC016740F8100029779 /* ReduceWhitespace.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ReduceWhitespace.h; sourceTree = "<group>"; };
0FF7168A15A3B231008F5DAA /* PropertyOffset.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PropertyOffset.h; sourceTree = "<group>"; };
0FF72992166AD347000F5BA3 /* ProfilerBytecode.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = ProfilerBytecode.cpp; path = profiler/ProfilerBytecode.cpp; sourceTree = "<group>"; };
0FF72993166AD347000F5BA3 /* ProfilerBytecode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ProfilerBytecode.h; path = profiler/ProfilerBytecode.h; sourceTree = "<group>"; };
......@@ -2627,11 +2631,11 @@
0F93329314CA7DC10085F3C6 /* CallLinkStatus.cpp */,
0F93329414CA7DC10085F3C6 /* CallLinkStatus.h */,
0F0B83B814BCF95B00885B4F /* CallReturnOffsetToBytecodeOffset.h */,
969A07900ED1D3AE00F1F681 /* CodeBlock.cpp */,
969A07910ED1D3AE00F1F681 /* CodeBlock.h */,
0F8F943D1667632D00D61971 /* CodeBlockHash.cpp */,
0F8F943E1667632D00D61971 /* CodeBlockHash.h */,
0F96EBB116676EF4008BADE3 /* CodeBlockWithJITType.h */,
969A07900ED1D3AE00F1F681 /* CodeBlock.cpp */,
969A07910ED1D3AE00F1F681 /* CodeBlock.h */,
0F8F9445166764EE00D61971 /* CodeOrigin.cpp */,
0FBD7E671447998F00481315 /* CodeOrigin.h */,
0F8F943F1667632D00D61971 /* CodeType.cpp */,
......@@ -2664,6 +2668,8 @@
0F93329914CA7DC10085F3C6 /* PutByIdStatus.cpp */,
0F93329A14CA7DC10085F3C6 /* PutByIdStatus.h */,
0F9FC8C114E1B5FB00D52AE0 /* PutKind.h */,
0FF60ABF16740F8100029779 /* ReduceWhitespace.cpp */,
0FF60AC016740F8100029779 /* ReduceWhitespace.h */,
0FF4276E159275D2004CB9FF /* ResolveGlobalStatus.cpp */,
0FF4276F159275D2004CB9FF /* ResolveGlobalStatus.h */,
A7AFC17715F7EFE30048F57B /* ResolveOperation.h */,
......@@ -2679,6 +2685,8 @@
0F766D3715AE4A1A008F363E /* StructureStubClearingWatchpoint.h */,
BCCF0D0B0EF0B8A500413C8F /* StructureStubInfo.cpp */,
BCCF0D070EF0AAB900413C8F /* StructureStubInfo.h */,
A79E781E15EECBA80047C855 /* UnlinkedCodeBlock.cpp */,
A79E781F15EECBA80047C855 /* UnlinkedCodeBlock.h */,
0F963B3613FC6FDE0002D9B2 /* ValueProfile.h */,
0F426A451460CBAB00131F8F /* ValueRecovery.h */,
0F426A461460CBAB00131F8F /* VirtualRegister.h */,
......@@ -2688,8 +2696,6 @@
0F93329914CA7DC10085F3C6 /* PutByIdStatus.cpp */,
0F93329A14CA7DC10085F3C6 /* PutByIdStatus.h */,
0F93329B14CA7DC10085F3C6 /* StructureSet.h */,
A79E781E15EECBA80047C855 /* UnlinkedCodeBlock.cpp */,
A79E781F15EECBA80047C855 /* UnlinkedCodeBlock.h */,
);
path = bytecode;
sourceTree = "<group>";
......@@ -3137,6 +3143,7 @@
0FF729BE166AD360000F5BA3 /* ProfilerExecutionCounter.h in Headers */,
0FF729BF166AD360000F5BA3 /* ProfilerOrigin.h in Headers */,
0FF729C0166AD360000F5BA3 /* ProfilerOriginStack.h in Headers */,
0FF60AC216740F8300029779 /* ReduceWhitespace.h in Headers */,
);
runOnlyForDeploymentPostprocessing = 0;
};
......@@ -3733,6 +3740,7 @@
0FF729B2166AD35C000F5BA3 /* ProfilerDatabase.cpp in Sources */,
0FF729B3166AD35C000F5BA3 /* ProfilerOrigin.cpp in Sources */,
0FF729B4166AD35C000F5BA3 /* ProfilerOriginStack.cpp in Sources */,
0FF60AC316740F8800029779 /* ReduceWhitespace.cpp in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
......
......@@ -67,6 +67,7 @@ SOURCES += \
bytecode/Opcode.cpp \
bytecode/PolymorphicPutByIdList.cpp \
bytecode/PutByIdStatus.cpp \
bytecode/ReduceWhitespace.cpp \
bytecode/ResolveGlobalStatus.cpp \
bytecode/SamplingTool.cpp \
bytecode/SpecialPointer.cpp \
......
......@@ -44,6 +44,7 @@
#include "JSNameScope.h"
#include "JSValue.h"
#include "LowLevelInterpreter.h"
#include "ReduceWhitespace.h"
#include "RepatchBuffer.h"
#include "SlotVisitorInlines.h"
#include <stdio.h>
......@@ -68,6 +69,30 @@ CodeBlockHash CodeBlock::hash() const
return CodeBlockHash(ownerExecutable()->source(), specializationKind());
}
String CodeBlock::sourceCodeForTools() const
{
if (codeType() != FunctionCode)
return ownerExecutable()->source().toString();
SourceProvider* provider = source();
FunctionExecutable* executable = jsCast<FunctionExecutable*>(ownerExecutable());
UnlinkedFunctionExecutable* unlinked = executable->unlinkedExecutable();
unsigned unlinkedStartOffset = unlinked->startOffset();
unsigned linkedStartOffset = executable->source().startOffset();
int delta = linkedStartOffset - unlinkedStartOffset;
StringBuilder builder;
builder.append("function ");
builder.append(provider->getRange(
delta + unlinked->functionStartOffset(),
delta + unlinked->startOffset() + unlinked->sourceLength()));
return builder.toString();
}
String CodeBlock::sourceCodeOnOneLine() const
{
return reduceWhitespace(sourceCodeForTools());
}
void CodeBlock::dumpAssumingJITType(PrintStream& out, JITCode::JITType jitType) const
{
out.print("#", hash(), ":[", RawPointer(this), "->", RawPointer(ownerExecutable()), ", ", jitType, codeType());
......@@ -527,7 +552,7 @@ void CodeBlock::dumpBytecode(PrintStream& out)
}
if (needsFullScopeChain() && codeType() == FunctionCode)
out.printf("; activation in r%d", activationRegister());
out.printf("\n\n");
out.print("\n\nSource: ", sourceCodeOnOneLine(), "\n\n");
const Instruction* begin = instructions().begin();
const Instruction* end = instructions().end();
......@@ -1690,9 +1715,6 @@ CodeBlock::CodeBlock(ScriptExecutable* ownerExecutable, UnlinkedCodeBlock* unlin
ASSERT(m_source);
setNumParameters(unlinkedCodeBlock->numParameters());
optimizeAfterWarmUp();
jitAfterWarmUp();
#if DUMP_CODE_BLOCK_STATISTICS
liveCodeBlockSet.add(this);
#endif
......@@ -1911,6 +1933,12 @@ CodeBlock::CodeBlock(ScriptExecutable* ownerExecutable, UnlinkedCodeBlock* unlin
}
m_instructions = WTF::RefCountedArray<Instruction>(instructions);
// Set optimization thresholds only after m_instructions is initialized, since these
// rely on the instruction count (and are in theory permitted to also inspect the
// instruction stream to more accurate assess the cost of tier-up).
optimizeAfterWarmUp();
jitAfterWarmUp();
if (Options::dumpGeneratedBytecodes())
dumpBytecode();
m_globalData->finishedCompiling(this);
......@@ -2911,6 +2939,196 @@ bool FunctionCodeBlock::jitCompileImpl(ExecState* exec)
}
#endif
unsigned CodeBlock::reoptimizationRetryCounter() const
{
ASSERT(m_reoptimizationRetryCounter <= Options::reoptimizationRetryCounterMax());
return m_reoptimizationRetryCounter;
}
void CodeBlock::countReoptimization()
{
m_reoptimizationRetryCounter++;
if (m_reoptimizationRetryCounter > Options::reoptimizationRetryCounterMax())
m_reoptimizationRetryCounter = Options::reoptimizationRetryCounterMax();
}
double CodeBlock::optimizationThresholdScalingFactor()
{
// This expression arises from doing a least-squares fit of
//
// F[x_] =: a * Sqrt[x + b] + Abs[c * x] + d
//
// against the data points:
//
// x F[x_]
// 10 0.9 (smallest reasonable code block)
// 200 1.0 (typical small-ish code block)
// 320 1.2 (something I saw in 3d-cube that I wanted to optimize)
// 1268 5.0 (something I saw in 3d-cube that I didn't want to optimize)
// 4000 5.5 (random large size, used to cause the function to converge to a shallow curve of some sort)
// 10000 6.0 (similar to above)
//
// I achieve the minimization using the following Mathematica code:
//
// MyFunctionTemplate[x_, a_, b_, c_, d_] := a*Sqrt[x + b] + Abs[c*x] + d
//
// samples = {{10, 0.9}, {200, 1}, {320, 1.2}, {1268, 5}, {4000, 5.5}, {10000, 6}}
//
// solution =
// Minimize[Plus @@ ((MyFunctionTemplate[#[[1]], a, b, c, d] - #[[2]])^2 & /@ samples),
// {a, b, c, d}][[2]]
//
// And the code below (to initialize a, b, c, d) is generated by:
//
// Print["const double " <> ToString[#[[1]]] <> " = " <>
// If[#[[2]] < 0.00001, "0.0", ToString[#[[2]]]] <> ";"] & /@ solution
//
// We've long known the following to be true:
// - Small code blocks are cheap to optimize and so we should do it sooner rather
// than later.
// - Large code blocks are expensive to optimize and so we should postpone doing so,
// and sometimes have a large enough threshold that we never optimize them.
// - The difference in cost is not totally linear because (a) just invoking the
// DFG incurs some base cost and (b) for large code blocks there is enough slop
// in the correlation between instruction count and the actual compilation cost
// that for those large blocks, the instruction count should not have a strong
// influence on our threshold.
//
// I knew the goals but I didn't know how to achieve them; so I picked an interesting
// example where the heuristics were right (code block in 3d-cube with instruction
// count 320, which got compiled early as it should have been) and one where they were
// totally wrong (code block in 3d-cube with instruction count 1268, which was expensive
// to compile and didn't run often enough to warrant compilation in my opinion), and
// then threw in additional data points that represented my own guess of what our
// heuristics should do for some round-numbered examples.
//
// The expression to which I decided to fit the data arose because I started with an
// affine function, and then did two things: put the linear part in an Abs to ensure
// that the fit didn't end up choosing a negative value of c (which would result in
// the function turning over and going negative for large x) and I threw in a Sqrt
// term because Sqrt represents my intution that the function should be more sensitive
// to small changes in small values of x, but less sensitive when x gets large.
// Note that the current fit essentially eliminates the linear portion of the
// expression (c == 0.0).
const double a = 0.061504;
const double b = 1.02406;
const double c = 0.0;
const double d = 0.825914;
double instructionCount = this->instructionCount();
ASSERT(instructionCount); // Make sure this is called only after we have an instruction stream; otherwise it'll just return the value of d, which makes no sense.
double result = d + a * sqrt(instructionCount + b) + c * instructionCount;
#if ENABLE(JIT_VERBOSE_OSR)
dataLog(*this, ": instruction count is ", instructionCount, ", scaling execution counter by ", result, "\n");
#endif
return result;
}
static int32_t clipThreshold(double threshold)
{
if (threshold < 1.0)
return 1;
if (threshold > static_cast<double>(std::numeric_limits<int32_t>::max()))
return std::numeric_limits<int32_t>::max();
return static_cast<int32_t>(threshold);
}
int32_t CodeBlock::counterValueForOptimizeAfterWarmUp()
{
return clipThreshold(
Options::thresholdForOptimizeAfterWarmUp() *
optimizationThresholdScalingFactor() *
(1 << reoptimizationRetryCounter()));
}
int32_t CodeBlock::counterValueForOptimizeAfterLongWarmUp()
{
return clipThreshold(
Options::thresholdForOptimizeAfterLongWarmUp() *
optimizationThresholdScalingFactor() *
(1 << reoptimizationRetryCounter()));
}
int32_t CodeBlock::counterValueForOptimizeSoon()
{
return clipThreshold(
Options::thresholdForOptimizeSoon() *
optimizationThresholdScalingFactor() *
(1 << reoptimizationRetryCounter()));
}
bool CodeBlock::checkIfOptimizationThresholdReached()
{
return m_jitExecuteCounter.checkIfThresholdCrossedAndSet(this);
}
void CodeBlock::optimizeNextInvocation()
{
m_jitExecuteCounter.setNewThreshold(0, this);
}
void CodeBlock::dontOptimizeAnytimeSoon()
{
m_jitExecuteCounter.deferIndefinitely();
}
void CodeBlock::optimizeAfterWarmUp()
{
m_jitExecuteCounter.setNewThreshold(counterValueForOptimizeAfterWarmUp(), this);
}
void CodeBlock::optimizeAfterLongWarmUp()
{
m_jitExecuteCounter.setNewThreshold(counterValueForOptimizeAfterLongWarmUp(), this);
}
void CodeBlock::optimizeSoon()
{
m_jitExecuteCounter.setNewThreshold(counterValueForOptimizeSoon(), this);
}
#if ENABLE(JIT)
uint32_t CodeBlock::adjustedExitCountThreshold(uint32_t desiredThreshold)
{
ASSERT(getJITType() == JITCode::DFGJIT);
// Compute this the lame way so we don't saturate. This is called infrequently
// enough that this loop won't hurt us.
unsigned result = desiredThreshold;
for (unsigned n = baselineVersion()->reoptimizationRetryCounter(); n--;) {
unsigned newResult = result << 1;
if (newResult < result)
return std::numeric_limits<uint32_t>::max();
result = newResult;
}
return result;
}
uint32_t CodeBlock::exitCountThresholdForReoptimization()
{
return adjustedExitCountThreshold(Options::osrExitCountForReoptimization());
}
uint32_t CodeBlock::exitCountThresholdForReoptimizationFromLoop()
{
return adjustedExitCountThreshold(Options::osrExitCountForReoptimizationFromLoop());
}
bool CodeBlock::shouldReoptimizeNow()
{
return osrExitCounter() >= exitCountThresholdForReoptimization();
}
bool CodeBlock::shouldReoptimizeFromLoopNow()
{
return osrExitCounter() >= exitCountThresholdForReoptimizationFromLoop();
}
#endif
#if ENABLE(VALUE_PROFILER)
ArrayProfile* CodeBlock::getArrayProfile(unsigned bytecodeOffset)
{
......
......@@ -132,6 +132,8 @@ namespace JSC {
UnlinkedCodeBlock* unlinkedCodeBlock() const { return m_unlinkedCode.get(); }
CodeBlockHash hash() const;
String sourceCodeForTools() const; // Not quite the actual source we parsed; this will do things like prefix the source for a function with a reified signature.
String sourceCodeOnOneLine() const; // As sourceCodeForTools(), but replaces all whitespace runs with a single space.
void dumpAssumingJITType(PrintStream&, JITCode::JITType) const;
void dump(PrintStream&) const;
......@@ -1009,28 +1011,12 @@ namespace JSC {
// When we observe a lot of speculation failures, we trigger a
// reoptimization. But each time, we increase the optimization trigger
// to avoid thrashing.
unsigned reoptimizationRetryCounter() const
{
ASSERT(m_reoptimizationRetryCounter <= Options::reoptimizationRetryCounterMax());
return m_reoptimizationRetryCounter;
}
void countReoptimization()
{
m_reoptimizationRetryCounter++;
if (m_reoptimizationRetryCounter > Options::reoptimizationRetryCounterMax())
m_reoptimizationRetryCounter = Options::reoptimizationRetryCounterMax();
}
unsigned reoptimizationRetryCounter() const;
void countReoptimization();
int32_t counterValueForOptimizeAfterWarmUp()
{
return Options::thresholdForOptimizeAfterWarmUp() << reoptimizationRetryCounter();
}
int32_t counterValueForOptimizeAfterLongWarmUp()
{
return Options::thresholdForOptimizeAfterLongWarmUp() << reoptimizationRetryCounter();
}
int32_t counterValueForOptimizeAfterWarmUp();
int32_t counterValueForOptimizeAfterLongWarmUp();
int32_t counterValueForOptimizeSoon();
int32_t* addressOfJITExecuteCounter()
{
......@@ -1048,28 +1034,19 @@ namespace JSC {
// Check if the optimization threshold has been reached, and if not,
// adjust the heuristics accordingly. Returns true if the threshold has
// been reached.
bool checkIfOptimizationThresholdReached()
{
return m_jitExecuteCounter.checkIfThresholdCrossedAndSet(this);
}
bool checkIfOptimizationThresholdReached();
// Call this to force the next optimization trigger to fire. This is
// rarely wise, since optimization triggers are typically more
// expensive than executing baseline code.
void optimizeNextInvocation()
{
m_jitExecuteCounter.setNewThreshold(0, this);
}
void optimizeNextInvocation();
// Call this to prevent optimization from happening again. Note that
// optimization will still happen after roughly 2^29 invocations,
// so this is really meant to delay that as much as possible. This
// is called if optimization failed, and we expect it to fail in
// the future as well.
void dontOptimizeAnytimeSoon()
{
m_jitExecuteCounter.deferIndefinitely();
}
void dontOptimizeAnytimeSoon();
// Call this to reinitialize the counter to its starting state,
// forcing a warm-up to happen before the next optimization trigger
......@@ -1077,17 +1054,11 @@ namespace JSC {
// makes sense to call this if an OSR exit occurred. Note that
// OSR exit code is code generated, so the value of the execute
// counter that this corresponds to is also available directly.
void optimizeAfterWarmUp()
{
m_jitExecuteCounter.setNewThreshold(counterValueForOptimizeAfterWarmUp(), this);
}
void optimizeAfterWarmUp();
// Call this to force an optimization trigger to fire only after
// a lot of warm-up.
void optimizeAfterLongWarmUp()
{
m_jitExecuteCounter.setNewThreshold(counterValueForOptimizeAfterLongWarmUp(), this);
}
void optimizeAfterLongWarmUp();
// Call this to cause an optimization trigger to fire soon, but
// not necessarily the next one. This makes sense if optimization
......@@ -1107,10 +1078,7 @@ namespace JSC {
// each return, as that would be superfluous. It only makes sense
// to trigger optimization if one of those functions becomes hot
// in the baseline code.
void optimizeSoon()
{
m_jitExecuteCounter.setNewThreshold(Options::thresholdForOptimizeSoon() << reoptimizationRetryCounter(), this);
}
void optimizeSoon();
uint32_t osrExitCounter() const { return m_osrExitCounter; }
......@@ -1121,40 +1089,11 @@ namespace JSC {
static ptrdiff_t offsetOfOSRExitCounter() { return OBJECT_OFFSETOF(CodeBlock, m_osrExitCounter); }
#if ENABLE(JIT)
uint32_t adjustedExitCountThreshold(uint32_t desiredThreshold)
{
ASSERT(getJITType() == JITCode::DFGJIT);
// Compute this the lame way so we don't saturate. This is called infrequently
// enough that this loop won't hurt us.
unsigned result = desiredThreshold;
for (unsigned n = baselineVersion()->reoptimizationRetryCounter(); n--;) {
unsigned newResult = result << 1;
if (newResult < result)
return std::numeric_limits<uint32_t>::max();
result = newResult;
}
return result;
}
uint32_t exitCountThresholdForReoptimization()
{
return adjustedExitCountThreshold(Options::osrExitCountForReoptimization());
}
uint32_t exitCountThresholdForReoptimizationFromLoop()
{
return adjustedExitCountThreshold(Options::osrExitCountForReoptimizationFromLoop());
}
bool shouldReoptimizeNow()
{
return osrExitCounter() >= exitCountThresholdForReoptimization();
}
bool shouldReoptimizeFromLoopNow()
{
return osrExitCounter() >= exitCountThresholdForReoptimizationFromLoop();
}
uint32_t adjustedExitCountThreshold(uint32_t desiredThreshold);
uint32_t exitCountThresholdForReoptimization();
uint32_t exitCountThresholdForReoptimizationFromLoop();
bool shouldReoptimizeNow();
bool shouldReoptimizeFromLoopNow();