Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

JIT CSE Optimization - Add a gymnasium environment for reinforcement learning #101856

Merged
merged 78 commits into from
May 9, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
78 commits
Select commit Hold shift + click to select a range
9d940a2
Initial code
leculver Apr 26, 2024
0c31d02
Add notes
leculver Apr 26, 2024
8a5acd5
Add CSE_HeuristicRLHook
leculver Apr 26, 2024
476063a
Move metric print location, double -> int
leculver Apr 26, 2024
5de67e1
Produce non-viable entries, fix output issue
leculver Apr 26, 2024
64b4821
Shuffle features by type
leculver Apr 26, 2024
0aa1d8d
Initial JitEnv - not yet working
leculver Apr 26, 2024
4f9fe2a
Change to snake_case
leculver Apr 27, 2024
fe5d334
Initial RL implementation with stable-baselines3
leculver Apr 27, 2024
dd762f8
Enable parallel processing, fix some errors
leculver Apr 27, 2024
690c4c4
Clean up train.py, allow algorithm selection
leculver Apr 27, 2024
b8fd437
Fix paths
leculver Apr 27, 2024
8ee8a0f
Fix issue with null result
leculver Apr 27, 2024
313788d
Save method indexes
leculver Apr 28, 2024
2d9e02f
Check if process is still running
leculver Apr 28, 2024
39a46b5
Up argument count before warning
leculver Apr 28, 2024
11b4346
Track more statistics on tensorboard
leculver Apr 28, 2024
678c5ee
Fix an issue where we didn't let the model know it shouldn't pick som…
leculver Apr 28, 2024
5d5f47e
Reward improvements
leculver Apr 28, 2024
c32f704
Update jitenv.py to remove unused import
leculver Apr 28, 2024
0f8b109
Fix inverted graph
leculver Apr 28, 2024
303c642
Split data into test/train
leculver Apr 28, 2024
81d8314
Refactor for clarity
leculver Apr 28, 2024
6265e40
Use numpy for randomness
leculver Apr 28, 2024
12ae7f5
Add open questions
leculver Apr 28, 2024
e735ee0
Fix a couple of model saving issues
leculver Apr 28, 2024
b3032a0
Refactor and cleanup
leculver Apr 28, 2024
b76896c
Add evaluate.py
leculver Apr 28, 2024
ecdde9b
Fix inverted test/train
leculver Apr 28, 2024
7982aa4
Add a way to get the probabilities of actions
leculver Apr 28, 2024
cb6dbab
Rename file
leculver Apr 28, 2024
8653120
Clean up imports
leculver Apr 28, 2024
ca067c1
Changed action space
leculver Apr 28, 2024
dd2702a
Add field validator for perf_score
leculver Apr 28, 2024
2c91107
Update applicability to ensure we have at least enough viable candida…
leculver Apr 28, 2024
430e5c9
Fix a few bugs with evaluate
leculver Apr 29, 2024
18dd0f1
Fix test/train split, some extra output
leculver Apr 29, 2024
0ec0e40
Remove dead code, simplify format
leculver Apr 29, 2024
191137b
Rename JitEnv -> JitCseEnv
leculver Apr 29, 2024
74f628d
More renames
leculver Apr 29, 2024
01cc413
Try to factor the observaiton space
leculver Apr 29, 2024
42593ff
Fix test/train split
leculver Apr 30, 2024
6343d01
Reward cleanup
leculver Apr 30, 2024
727d869
Remove 0 perfscore check
leculver Apr 30, 2024
f63f576
Enable deep rewards
leculver Apr 30, 2024
b629873
Fix issue where jit failed and produced None method
leculver Apr 30, 2024
0779315
Simplify deeper rewards
leculver Apr 30, 2024
b2c76e4
Update todo
leculver Apr 30, 2024
87341a2
Add reward customization
leculver Apr 30, 2024
adbd9de
Clean up __all__
leculver Apr 30, 2024
f799976
Fix issue where we would JIT the first CSE candidate in reset
leculver Apr 30, 2024
dbc6902
Add two new features, emit selected sequence
leculver Apr 30, 2024
bdf9761
Jit one less method per cse chosen in deep rewards
leculver Apr 30, 2024
17dead9
Use info dictionary instead of a specific state
leculver Apr 30, 2024
c7647fa
Fix segfault due to null variable
leculver May 1, 2024
db2566a
Add superpmi_context
leculver May 1, 2024
b1cfb46
Add tensorboard entry for invalid choices, clear results
leculver May 1, 2024
58d8e16
Close the environment
leculver May 1, 2024
453dbde
Add documentation for JIT changes
leculver May 1, 2024
fa6c60d
Rename method
leculver May 1, 2024
eb023e2
Normalize observation
leculver May 1, 2024
5dbbc51
Set return type hint for clarity
leculver May 1, 2024
b6b32d9
Add RemoveFeaturesWrapper
leculver May 1, 2024
d10a28b
Update docstring
leculver May 1, 2024
26d9ed3
Rename function
leculver May 1, 2024
5e39ded
Move feature normalization to a wrapper
leculver May 2, 2024
7e39ed3
Remove import
leculver May 2, 2024
f880166
Fix warning
leculver May 2, 2024
b1ba57e
Fix Windows issue
leculver May 3, 2024
333236b
Properly log when using A2C
leculver May 3, 2024
8f277ac
Add readme
leculver May 3, 2024
5e399ce
Change argument name
leculver May 3, 2024
566c27a
Remove whitespace change
leculver May 3, 2024
80bb745
Format fixes
leculver May 3, 2024
b54e32f
Fix formatting
leculver May 3, 2024
6b9bdac
Update readme: Fix grammar, add a note about evaluation
leculver May 3, 2024
dee6f4a
Fixed incorrect filename in readme
leculver May 3, 2024
05d8574
Save more data to .json in preparation of other model kinds
leculver May 7, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/coreclr/jit/compiler.h
Original file line number Diff line number Diff line change
Expand Up @@ -2541,6 +2541,7 @@ class Compiler
friend class CSE_HeuristicReplay;
friend class CSE_HeuristicRL;
friend class CSE_HeuristicParameterized;
friend class CSE_HeuristicRLHook;
friend class CSE_Heuristic;
friend class CodeGenInterface;
friend class CodeGen;
Expand Down
9 changes: 9 additions & 0 deletions src/coreclr/jit/jitconfigvalues.h
Original file line number Diff line number Diff line change
Expand Up @@ -498,6 +498,15 @@ CONFIG_STRING(JitRLCSEAlpha, W("JitRLCSEAlpha"))
// If nonzero, dump candidate feature values
CONFIG_INTEGER(JitRLCSECandidateFeatures, W("JitRLCSECandidateFeatures"), 0)

// Enable CSE_HeuristicRLHook
CONFIG_INTEGER(JitRLHook, W("JitRLHook"), 0) // If 1, emit RL callbacks

// If 1, emit feature column names
CONFIG_INTEGER(JitRLHookEmitFeatureNames, W("JitRLHookEmitFeatureNames"), 0)

// A list of CSEs to choose, in the order they should be applied.
CONFIG_STRING(JitRLHookCSEDecisions, W("JitRLHookCSEDecisions"))

#if !defined(DEBUG) && !defined(_DEBUG)
RELEASE_CONFIG_INTEGER(JitEnableNoWayAssert, W("JitEnableNoWayAssert"), 0)
#else // defined(DEBUG) || defined(_DEBUG)
Expand Down
305 changes: 304 additions & 1 deletion src/coreclr/jit/optcse.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2973,6 +2973,298 @@ void CSE_HeuristicParameterized::DumpChoices(ArrayStack<Choice>& choices, CSEdsc

#ifdef DEBUG

//------------------------------------------------------------------------
// CSE_HeuristicRLHook: a generic 'hook' for driving CSE decisions out of
// process using reinforcement learning
//
// Arguments;
// pCompiler - compiler instance
//
// Notes:
// This creates a hook to control CSE decisions from an external process
// when JitRLHook=1 is set. This will cause the JIT to emit a series of
// feature building blocks for each CSE in the method. Feature names for
// these values can be found by setting JitRLHookEmitFeatureNames=1. To
// control the CSE decisions, set JitRLHookCSEDecisions with a sequence
// of CSE indices to apply.
//
// This hook is only available in debug/checked builds, and does not
// contain any machine learning code.
//
CSE_HeuristicRLHook::CSE_HeuristicRLHook(Compiler* pCompiler)
: CSE_HeuristicCommon(pCompiler)
{
}

//------------------------------------------------------------------------
// ConsiderTree: check if this tree can be a CSE candidate
//
// Arguments:
// tree - tree in question
// isReturn - true if tree is part of a return statement
//
// Returns:
// true if this tree can be a CSE
bool CSE_HeuristicRLHook::ConsiderTree(GenTree* tree, bool isReturn)
{
return CanConsiderTree(tree, isReturn);
}

//------------------------------------------------------------------------
// ConsiderCandidates: examine candidates and perform CSEs.
// This simply defers to the JitRLHookCSEDecisions config value.
//
void CSE_HeuristicRLHook::ConsiderCandidates()
{
if (JitConfig.JitRLHookCSEDecisions() != nullptr)
{
ConfigIntArray JitRLHookCSEDecisions;
JitRLHookCSEDecisions.EnsureInit(JitConfig.JitRLHookCSEDecisions());

unsigned cnt = m_pCompiler->optCSECandidateCount;
for (unsigned i = 0; i < JitRLHookCSEDecisions.GetLength(); i++)
{
const int index = JitRLHookCSEDecisions.GetData()[i];
if ((index < 0) || (index >= (int)cnt))
{
JITDUMP("Invalid candidate number %d\n", index + 1);
continue;
}

CSEdsc* const dsc = m_pCompiler->optCSEtab[index];
if (!dsc->IsViable())
{
JITDUMP("Abandoned " FMT_CSE " -- not viable\n", dsc->csdIndex);
continue;
}

const int attempt = m_pCompiler->optCSEattempt++;
CSE_Candidate candidate(this, dsc);

JITDUMP("\nRLHook attempting " FMT_CSE "\n", candidate.CseIndex());
JITDUMP("CSE Expression : \n");
JITDUMPEXEC(m_pCompiler->gtDispTree(candidate.Expr()));
JITDUMP("\n");

PerformCSE(&candidate);
madeChanges = true;
}
}
}

//------------------------------------------------------------------------
// DumpMetrics: write out features for each CSE candidate
// Format:
// featureNames <comma separated list of feature names>
// features #<CSE index>,<comma separated list of feature values>
// seq <comma separated list of CSE indices>
//
// Notes:
// featureNames are emitted only if JitRLHookEmitFeatureNames is set.
// features are 0 indexed, and the index is the first value, following #.
// seq is a comma separated list of CSE indices that were applied, or
// omitted if none were selected
//
void CSE_HeuristicRLHook::DumpMetrics()
{
// Feature names, if requested
if (JitConfig.JitRLHookEmitFeatureNames() > 0)
{
printf(" featureNames ");
for (int i = 0; i < maxFeatures; i++)
{
printf("%s%s", (i == 0) ? "" : ",", s_featureNameAndType[i]);
}
}

// features
for (unsigned i = 0; i < m_pCompiler->optCSECandidateCount; i++)
{
CSEdsc* const cse = m_pCompiler->optCSEtab[i];

int features[maxFeatures];
GetFeatures(cse, features);

printf(" features #%i", cse->csdIndex);
for (int j = 0; j < maxFeatures; j++)
{
printf(",%d", features[j]);
}
}

// The selected sequence of CSEs that were applied
if (JitConfig.JitRLHookCSEDecisions() != nullptr)
{
ConfigIntArray JitRLHookCSEDecisions;
JitRLHookCSEDecisions.EnsureInit(JitConfig.JitRLHookCSEDecisions());

if (JitRLHookCSEDecisions.GetLength() > 0)
{
printf(" seq ");
for (unsigned i = 0; i < JitRLHookCSEDecisions.GetLength(); i++)
{
printf("%s%d", (i == 0) ? "" : ",", JitRLHookCSEDecisions.GetData()[i]);
}
}
}
}

//------------------------------------------------------------------------
// GetFeatures: extract features for this CSE
// Arguments:
// cse - cse descriptor
// features - array to fill in with feature values, this must be of length
// maxFeatures or greater
//
// Notes:
// Features are intended to be building blocks of "real" features that
// are further defined and refined in the machine learning model. That
// means that each "feature" here is a simple value and not a composite
// of multiple values.
//
// Features do not need to be stable across builds, they can be changed,
// added, or removed. However, the corresponding code needs to be updated
// to match: src/coreclr/scripts/cse_ml/jitml/method_context.py
// See src/coreclr/scripts/cse_ml/README.md for more information.
//
void CSE_HeuristicRLHook::GetFeatures(CSEdsc* cse, int* features)
{
assert(cse != nullptr);
assert(features != nullptr);
CSE_Candidate candidate(this, cse);

int enregCount = 0;
for (unsigned trackedIndex = 0; trackedIndex < m_pCompiler->lvaTrackedCount; trackedIndex++)
{
LclVarDsc* varDsc = m_pCompiler->lvaGetDescByTrackedIndex(trackedIndex);
var_types varTyp = varDsc->TypeGet();

// Locals with no references aren't enregistered
if (varDsc->lvRefCnt() == 0)
{
continue;
}

// Some LclVars always have stack homes
if (varDsc->lvDoNotEnregister)
{
continue;
}

if (!varTypeIsFloating(varTyp))
{
enregCount++; // The primitive types, including TYP_SIMD types use one register

#ifndef TARGET_64BIT
if (varTyp == TYP_LONG)
{
enregCount++; // on 32-bit targets longs use two registers
}
#endif
}
}

const unsigned numBBs = m_pCompiler->fgBBcount;
bool isMakeCse = false;
unsigned minPostorderNum = numBBs;
unsigned maxPostorderNum = 0;
BasicBlock* minPostorderBlock = nullptr;
BasicBlock* maxPostorderBlock = nullptr;
for (treeStmtLst* treeList = cse->csdTreeList; treeList != nullptr; treeList = treeList->tslNext)
{
BasicBlock* const treeBlock = treeList->tslBlock;
unsigned postorderNum = treeBlock->bbPostorderNum;
if (postorderNum < minPostorderNum)
{
minPostorderNum = postorderNum;
minPostorderBlock = treeBlock;
}

if (postorderNum > maxPostorderNum)
{
maxPostorderNum = postorderNum;
maxPostorderBlock = treeBlock;
}

isMakeCse |= ((treeList->tslTree->gtFlags & GTF_MAKE_CSE) != 0);
}

const unsigned blockSpread = maxPostorderNum - minPostorderNum;

int type = rlHookTypeOther;
if (candidate.Expr()->TypeIs(TYP_INT))
{
type = rlHookTypeInt;
}
else if (candidate.Expr()->TypeIs(TYP_LONG))
{
type = rlHookTypeLong;
}
else if (candidate.Expr()->TypeIs(TYP_FLOAT))
{
type = rlHookTypeFloat;
}
else if (candidate.Expr()->TypeIs(TYP_DOUBLE))
{
type = rlHookTypeDouble;
}
else if (candidate.Expr()->TypeIs(TYP_STRUCT))
{
type = rlHookTypeStruct;
}

#ifdef FEATURE_SIMD
else if (varTypeIsSIMD(candidate.Expr()->TypeGet()))
{
type = rlHookTypeSimd;
}
#ifdef TARGET_XARCH
else if (candidate.Expr()->TypeIs(TYP_SIMD32, TYP_SIMD64))
{
type = rlHookTypeSimd;
}
#endif
#endif

int i = 0;
features[i++] = type;
features[i++] = cse->IsViable() ? 1 : 0;
features[i++] = cse->csdLiveAcrossCall ? 1 : 0;
features[i++] = cse->csdTree->OperIsConst() ? 1 : 0;
features[i++] = cse->csdIsSharedConst ? 1 : 0;
features[i++] = isMakeCse ? 1 : 0;
features[i++] = ((cse->csdTree->gtFlags & GTF_CALL) != 0) ? 1 : 0;
features[i++] = cse->csdTree->OperIs(GT_ADD, GT_NOT, GT_MUL, GT_LSH) ? 1 : 0;
features[i++] = cse->csdTree->GetCostEx();
features[i++] = cse->csdTree->GetCostSz();
features[i++] = cse->csdUseCount;
features[i++] = cse->csdDefCount;
features[i++] = (int)cse->csdUseWtCnt;
features[i++] = (int)cse->csdDefWtCnt;
features[i++] = cse->numDistinctLocals;
features[i++] = cse->numLocalOccurrences;
features[i++] = numBBs;
features[i++] = blockSpread;
features[i++] = enregCount;

assert(i <= maxFeatures);

for (; i < maxFeatures; i++)
{
features[i] = 0;
}
}

// These need to match the features above, and match the field name of MethodContext
// in src/coreclr/scripts/cse_ml/jitml/method_context.py
const char* const CSE_HeuristicRLHook::s_featureNameAndType[] = {
"type", "viable", "live_across_call", "const",
"shared_const", "make_cse", "has_call", "containable",
"cost_ex", "cost_sz", "use_count", "def_count",
"use_wt_cnt", "def_wt_cnt", "distinct_locals", "local_occurrences",
"bb_count", "block_spread", "enreg_count",
};

//------------------------------------------------------------------------
// CSE_HeuristicRL: construct RL CSE heuristic
//
Expand Down Expand Up @@ -5165,9 +5457,20 @@ CSE_HeuristicCommon* Compiler::optGetCSEheuristic()

// Enable optional policies
//
// RL takes precedence
// RL hook takes precedence
//
if (optCSEheuristic == nullptr)
{
bool useRLHook = (JitConfig.JitRLHook() > 0);

if (useRLHook)
{
optCSEheuristic = new (this, CMK_CSE) CSE_HeuristicRLHook(this);
}
}

// then RL
if (optCSEheuristic == nullptr)
{
bool useRLHeuristic = (JitConfig.JitRLCSE() != nullptr);

Expand Down
43 changes: 43 additions & 0 deletions src/coreclr/jit/optcse.h
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,49 @@ class CSE_HeuristicParameterized : public CSE_HeuristicCommon

#ifdef DEBUG

// General Reinforcement Learning CSE heuristic hook.
//
// Produces a wide set of data to train a RL model.
// Consumes the decisions made by a model to perform CSEs.
//
class CSE_HeuristicRLHook : public CSE_HeuristicCommon
{
private:
static const char* const s_featureNameAndType[];

void GetFeatures(CSEdsc* cse, int* features);

enum
{
maxFeatures = 19,
};

enum
{
rlHookTypeOther = 0,
rlHookTypeInt = 1,
rlHookTypeLong = 2,
rlHookTypeFloat = 3,
rlHookTypeDouble = 4,
rlHookTypeStruct = 5,
rlHookTypeSimd = 6,
};

public:
CSE_HeuristicRLHook(Compiler*);
void ConsiderCandidates();
bool ConsiderTree(GenTree* tree, bool isReturn);

const char* Name() const
{
return "RL Hook CSE Heuristic";
}

#ifdef DEBUG
virtual void DumpMetrics();
#endif
};

// Reinforcement Learning CSE heuristic
//
// Uses a "linear" feature model with
Expand Down
2 changes: 2 additions & 0 deletions src/coreclr/scripts/cse_ml/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# The root .gitignore doens't mark this as ignored:
__pycache__
Loading
Loading