1af732203SDimitry Andric //===- DevelopmentModeInlineAdvisor.cpp - runtime-loadable model runner --===// 2af732203SDimitry Andric // 3af732203SDimitry Andric // The LLVM Compiler Infrastructure 4af732203SDimitry Andric // 5af732203SDimitry Andric // This file is distributed under the University of Illinois Open Source 6af732203SDimitry Andric // License. See LICENSE.TXT for details. 7af732203SDimitry Andric // 8af732203SDimitry Andric //===----------------------------------------------------------------------===// 9af732203SDimitry Andric // 10af732203SDimitry Andric // This file implements a model runner using Tensorflow C APIs, allowing the 11af732203SDimitry Andric // loading of a model from a command line option. 12af732203SDimitry Andric // 13af732203SDimitry Andric //===----------------------------------------------------------------------===// 14af732203SDimitry Andric #include "llvm/Config/config.h" 15af732203SDimitry Andric #if defined(LLVM_HAVE_TF_API) 16af732203SDimitry Andric 17af732203SDimitry Andric #include "llvm/Analysis/CallGraph.h" 18af732203SDimitry Andric #include "llvm/Analysis/InlineSizeEstimatorAnalysis.h" 19af732203SDimitry Andric #include "llvm/Analysis/MLInlineAdvisor.h" 20af732203SDimitry Andric #include "llvm/Analysis/Utils/TFUtils.h" 21af732203SDimitry Andric #include "llvm/IR/LLVMContext.h" 22af732203SDimitry Andric #include "llvm/Support/CommandLine.h" 23af732203SDimitry Andric #include "llvm/Support/ManagedStatic.h" 24af732203SDimitry Andric 25af732203SDimitry Andric #include <vector> 26af732203SDimitry Andric 27af732203SDimitry Andric using namespace llvm; 28af732203SDimitry Andric 29af732203SDimitry Andric static cl::opt<std::string> TrainingLog( 30af732203SDimitry Andric "training-log", cl::Hidden, 31af732203SDimitry Andric cl::desc("Path where the development - mode inlining log is saved.")); 32af732203SDimitry Andric 33af732203SDimitry Andric static cl::opt<std::string> TFModelUnderTrainingPath( 34af732203SDimitry Andric "ml-inliner-model-under-training", cl::Hidden, 35af732203SDimitry Andric cl::desc(R"(Path to SavedModel from the previous training iteration. 36af732203SDimitry Andric The directory is also expected to contain a JSON specification of the 37af732203SDimitry Andric outputs expected to be logged, where the first entry must be the 38af732203SDimitry Andric inlining decision. The file containing the specification should be 39af732203SDimitry Andric called output_spec.json. The expected JSON value is an array of 40af732203SDimitry Andric dictionaries. Each dictionary should have 2 keys: 41af732203SDimitry Andric 42af732203SDimitry Andric - "tensor_spec, followed by the TensorSpec description of the 43af732203SDimitry Andric output; and 44af732203SDimitry Andric - "logging_name", a string indicating the name to use when 45af732203SDimitry Andric logging the output values. 46af732203SDimitry Andric 47af732203SDimitry Andric Example: 48af732203SDimitry Andric [ 49af732203SDimitry Andric { 50af732203SDimitry Andric "logging_name" : "some_name", 51af732203SDimitry Andric "tensor_spec" : { 52af732203SDimitry Andric "name" : "model_name", 53af732203SDimitry Andric "port" : 0, 54af732203SDimitry Andric "shape" : [2, 3], 55af732203SDimitry Andric "type" : "float" 56af732203SDimitry Andric } 57af732203SDimitry Andric } 58af732203SDimitry Andric ] 59af732203SDimitry Andric 60af732203SDimitry Andric The first value must always correspond to the decision.)")); 61af732203SDimitry Andric 62af732203SDimitry Andric static cl::opt<std::string> TFOutputSpecOverride( 63af732203SDimitry Andric "ml-inliner-output-spec-override", cl::Hidden, 64af732203SDimitry Andric cl::desc("Override the path to the output spec json file. See " 65af732203SDimitry Andric "-ml-inliner-model-under-training documentation for the " 66af732203SDimitry Andric "specification of that file.")); 67af732203SDimitry Andric 68af732203SDimitry Andric static cl::opt<std::string> TFFeedPrefix("ml-inliner-trained-model-feed-prefix", 69af732203SDimitry Andric cl::Hidden, cl::init("action_"), 70af732203SDimitry Andric cl::desc("Prefix for feature names.")); 71af732203SDimitry Andric 72af732203SDimitry Andric namespace { 73af732203SDimitry Andric /// An InlineEvent, used by TrainingLogger. 74af732203SDimitry Andric struct InlineEvent { 75af732203SDimitry Andric /// What the default policy's decision would have been. 76af732203SDimitry Andric int64_t DefaultDecision = 0; 77af732203SDimitry Andric 78af732203SDimitry Andric /// What we advised. When training off the default policy, this is the same as 79af732203SDimitry Andric /// DefaultDecision. 80af732203SDimitry Andric int64_t AdvisedDecision = 0; 81af732203SDimitry Andric 82af732203SDimitry Andric /// What actually happened. This would be 'false' in the case of an inline 83af732203SDimitry Andric /// error, even if AdvisedDecision were true, otherwise it agrees with 84af732203SDimitry Andric /// AdvisedDecision. 85af732203SDimitry Andric bool Effect = false; 86af732203SDimitry Andric 87af732203SDimitry Andric /// What the change in size was: size_after - size_before 88af732203SDimitry Andric int64_t Reward = 0; 89af732203SDimitry Andric }; 90af732203SDimitry Andric 91af732203SDimitry Andric /// Collect data we may use for training a model, and write it as a textual 92af732203SDimitry Andric /// Tensorflow SequenceExample 93af732203SDimitry Andric /// (https://www.tensorflow.org/api_docs/python/tf/train/SequenceExample) 94af732203SDimitry Andric /// protobuf (https://developers.google.com/protocol-buffers). 95af732203SDimitry Andric /// Because this is a protobuf, we cannot just stream the events as they come. 96af732203SDimitry Andric /// Internally, TrainingLogger stores data in column-major format, because that 97af732203SDimitry Andric /// lines up with how TF SequenceExample represents it. 98af732203SDimitry Andric class ModelUnderTrainingRunner; 99af732203SDimitry Andric class TrainingLogger final { 100af732203SDimitry Andric public: 101af732203SDimitry Andric TrainingLogger(StringRef LogFileName, const ModelUnderTrainingRunner *MUTR); 102af732203SDimitry Andric 103af732203SDimitry Andric /// Log one inlining event. 104af732203SDimitry Andric void logInlineEvent(const InlineEvent &Event, 105af732203SDimitry Andric const MLModelRunner &ModelRunner); 106af732203SDimitry Andric 107af732203SDimitry Andric /// Print the stored tensors. 108af732203SDimitry Andric void print(); 109af732203SDimitry Andric 110af732203SDimitry Andric private: 111af732203SDimitry Andric StringRef LogFileName; 112af732203SDimitry Andric const ModelUnderTrainingRunner *const MUTR; 113af732203SDimitry Andric std::unique_ptr<Logger> L; 114af732203SDimitry Andric std::vector<bool> Effects; 115af732203SDimitry Andric /// There's at least one output. We'll set this to a different value if MUTR 116af732203SDimitry Andric /// is avaliable. 117af732203SDimitry Andric size_t OutputCount = 1; 118af732203SDimitry Andric /// Set these 2 clearly OOB, to make sure we set them later. 119af732203SDimitry Andric size_t DefaultDecisionPos = std::numeric_limits<size_t>::max(); 120af732203SDimitry Andric size_t DecisionPos = std::numeric_limits<size_t>::max(); 121af732203SDimitry Andric }; 122af732203SDimitry Andric 123af732203SDimitry Andric /// An extension of the MLInlineAdvisor for the 'development' mode, targeting 124af732203SDimitry Andric /// the offline training scenario. Note that training happens outside of the 125af732203SDimitry Andric /// compiler, this facility is concerned with producing training data ("logs"). 126af732203SDimitry Andric /// This InlineAdvisor can operate in the following modes: 127af732203SDimitry Andric /// 128af732203SDimitry Andric /// 1) collect logs for the default policy. This is useful for bootstrapping 129af732203SDimitry Andric /// training, which will be considerably faster by starting from a reasonable 130af732203SDimitry Andric /// policy. 131af732203SDimitry Andric /// 132af732203SDimitry Andric /// 2) collect logs for the ML policy, using a model from a previous 133af732203SDimitry Andric /// training. Potentially, that model uses internally some small random 134af732203SDimitry Andric /// perturbation of its weights, to induce exploration (setting this up is the 135af732203SDimitry Andric /// responsibility of the training algorithm). The logs would then be used to 136af732203SDimitry Andric /// retrain and improve on this model. 137af732203SDimitry Andric /// 138af732203SDimitry Andric /// 3) use the provided model, with no logging. This is useful for end to end 139af732203SDimitry Andric /// validation - the model, in this case, is a release candidate and shouldn't 140af732203SDimitry Andric /// have random perturbations. It is a convenience feature: rather than needing 141af732203SDimitry Andric /// to take the release candidate model and compile it in 'release' mode, 142af732203SDimitry Andric /// validate it, then potentially discard it, it's easier to just pass the model 143af732203SDimitry Andric /// to the compiler, albeit compilation would be slower, as a one-off. Once the 144af732203SDimitry Andric /// model behaves satisfactorily, it can be compiled AOT, for efficiency, in 145af732203SDimitry Andric /// release mode. The expectation is that a well-trained model provides a good 146af732203SDimitry Andric /// policy over a sufficiently diverse codebase, over many changes (i.e. 147af732203SDimitry Andric /// training happens seldom). 148af732203SDimitry Andric class DevelopmentModeMLInlineAdvisor : public MLInlineAdvisor { 149af732203SDimitry Andric public: 150af732203SDimitry Andric DevelopmentModeMLInlineAdvisor( 151af732203SDimitry Andric Module &M, ModuleAnalysisManager &MAM, 152af732203SDimitry Andric std::unique_ptr<MLModelRunner> ModelRunner, 153af732203SDimitry Andric std::function<bool(CallBase &)> GetDefaultAdvice, bool IsDoingInference, 154af732203SDimitry Andric std::unique_ptr<TrainingLogger> Logger); 155af732203SDimitry Andric 156af732203SDimitry Andric size_t getTotalSizeEstimate(); 157af732203SDimitry Andric 158af732203SDimitry Andric virtual ~DevelopmentModeMLInlineAdvisor(); updateNativeSizeEstimate(int64_t Change)159af732203SDimitry Andric void updateNativeSizeEstimate(int64_t Change) { 160af732203SDimitry Andric *CurrentNativeSize += Change; 161af732203SDimitry Andric } 162af732203SDimitry Andric void resetNativeSize(Function *F) { 163*5f7ddb14SDimitry Andric PreservedAnalyses PA = PreservedAnalyses::all(); 164*5f7ddb14SDimitry Andric PA.abandon<InlineSizeEstimatorAnalysis>(); 165*5f7ddb14SDimitry Andric FAM.invalidate(*F, PA); 166af732203SDimitry Andric } 167af732203SDimitry Andric 168af732203SDimitry Andric std::unique_ptr<MLInlineAdvice> 169af732203SDimitry Andric getAdviceFromModel(CallBase &CB, OptimizationRemarkEmitter &ORE) override; 170af732203SDimitry Andric 171af732203SDimitry Andric Optional<size_t> getNativeSizeEstimate(const Function &F) const; 172af732203SDimitry Andric 173af732203SDimitry Andric private: 174af732203SDimitry Andric bool isLogging() const { return !!Logger; } 175af732203SDimitry Andric std::unique_ptr<MLInlineAdvice> getMandatoryAdviceImpl(CallBase &CB) override; 176af732203SDimitry Andric 177af732203SDimitry Andric std::function<bool(CallBase &)> GetDefaultAdvice; 178af732203SDimitry Andric const bool IsDoingInference; 179af732203SDimitry Andric std::unique_ptr<TrainingLogger> Logger; 180af732203SDimitry Andric 181af732203SDimitry Andric const Optional<int32_t> InitialNativeSize; 182af732203SDimitry Andric Optional<int32_t> CurrentNativeSize; 183af732203SDimitry Andric }; 184af732203SDimitry Andric 185af732203SDimitry Andric /// A variant of MLInlineAdvice that tracks all non-trivial inlining 186af732203SDimitry Andric /// decisions, for training/logging. 187af732203SDimitry Andric class LoggingMLInlineAdvice : public MLInlineAdvice { 188af732203SDimitry Andric public: 189af732203SDimitry Andric LoggingMLInlineAdvice(DevelopmentModeMLInlineAdvisor *Advisor, CallBase &CB, 190af732203SDimitry Andric OptimizationRemarkEmitter &ORE, bool Recommendation, 191af732203SDimitry Andric TrainingLogger &Logger, 192af732203SDimitry Andric Optional<size_t> CallerSizeEstimateBefore, 193af732203SDimitry Andric Optional<size_t> CalleeSizeEstimateBefore, 194af732203SDimitry Andric bool DefaultDecision, bool Mandatory = false) 195af732203SDimitry Andric : MLInlineAdvice(Advisor, CB, ORE, Recommendation), Logger(Logger), 196af732203SDimitry Andric CallerSizeEstimateBefore(CallerSizeEstimateBefore), 197af732203SDimitry Andric CalleeSizeEstimateBefore(CalleeSizeEstimateBefore), 198af732203SDimitry Andric DefaultDecision(DefaultDecision), Mandatory(Mandatory) {} 199af732203SDimitry Andric 200af732203SDimitry Andric virtual ~LoggingMLInlineAdvice() = default; 201af732203SDimitry Andric 202af732203SDimitry Andric private: 203af732203SDimitry Andric DevelopmentModeMLInlineAdvisor *getAdvisor() const { 204af732203SDimitry Andric return static_cast<DevelopmentModeMLInlineAdvisor *>(Advisor); 205af732203SDimitry Andric } 206af732203SDimitry Andric void recordInliningImpl() override { 207af732203SDimitry Andric MLInlineAdvice::recordInliningImpl(); 208af732203SDimitry Andric getAdvisor()->resetNativeSize(Caller); 209af732203SDimitry Andric int Reward = std::numeric_limits<int>::max(); 210af732203SDimitry Andric if (InlineSizeEstimatorAnalysis::isEvaluatorRequested() && 211af732203SDimitry Andric !getAdvisor()->isForcedToStop()) { 212af732203SDimitry Andric int NativeSizeAfter = *getAdvisor()->getNativeSizeEstimate(*Caller) + 213af732203SDimitry Andric *CalleeSizeEstimateBefore; 214af732203SDimitry Andric Reward = NativeSizeAfter - 215af732203SDimitry Andric (*CallerSizeEstimateBefore + *CalleeSizeEstimateBefore); 216af732203SDimitry Andric getAdvisor()->updateNativeSizeEstimate(Reward); 217af732203SDimitry Andric } 218af732203SDimitry Andric log(Reward, /*Success=*/true); 219af732203SDimitry Andric } 220af732203SDimitry Andric 221af732203SDimitry Andric void recordInliningWithCalleeDeletedImpl() override { 222af732203SDimitry Andric MLInlineAdvice::recordInliningWithCalleeDeletedImpl(); 223af732203SDimitry Andric getAdvisor()->resetNativeSize(Caller); 224af732203SDimitry Andric if (InlineSizeEstimatorAnalysis::isEvaluatorRequested() && 225af732203SDimitry Andric !getAdvisor()->isForcedToStop()) { 226af732203SDimitry Andric int NativeSizeAfter = *getAdvisor()->getNativeSizeEstimate(*Caller); 227af732203SDimitry Andric int Reward = NativeSizeAfter - 228af732203SDimitry Andric (*CallerSizeEstimateBefore + *CalleeSizeEstimateBefore); 229af732203SDimitry Andric getAdvisor()->updateNativeSizeEstimate(Reward); 230af732203SDimitry Andric log(Reward, /*Success=*/true); 231af732203SDimitry Andric } 232af732203SDimitry Andric } 233af732203SDimitry Andric 234af732203SDimitry Andric void recordUnsuccessfulInliningImpl(const InlineResult &Result) override { 235af732203SDimitry Andric MLInlineAdvice::recordUnsuccessfulInliningImpl(Result); 236af732203SDimitry Andric log(NoReward, /*Success=*/false); 237af732203SDimitry Andric } 238af732203SDimitry Andric 239af732203SDimitry Andric void recordUnattemptedInliningImpl() override { 240af732203SDimitry Andric MLInlineAdvice::recordUnattemptedInliningImpl(); 241af732203SDimitry Andric log(NoReward, /*Success=*/false); 242af732203SDimitry Andric } 243af732203SDimitry Andric 244af732203SDimitry Andric void log(int64_t Reward, bool Success) { 245af732203SDimitry Andric if (Mandatory) 246af732203SDimitry Andric return; 247af732203SDimitry Andric InlineEvent Event; 248af732203SDimitry Andric Event.AdvisedDecision = isInliningRecommended(); 249af732203SDimitry Andric Event.DefaultDecision = DefaultDecision; 250af732203SDimitry Andric Event.Effect = Success; 251af732203SDimitry Andric Event.Reward = Reward; 252af732203SDimitry Andric Logger.logInlineEvent(Event, getAdvisor()->getModelRunner()); 253af732203SDimitry Andric } 254af732203SDimitry Andric 255af732203SDimitry Andric static const int64_t NoReward = 0; 256af732203SDimitry Andric TrainingLogger &Logger; 257af732203SDimitry Andric const Optional<size_t> CallerSizeEstimateBefore; 258af732203SDimitry Andric const Optional<size_t> CalleeSizeEstimateBefore; 259af732203SDimitry Andric const int64_t DefaultDecision; 260af732203SDimitry Andric const int64_t Mandatory; 261af732203SDimitry Andric }; 262af732203SDimitry Andric 263af732203SDimitry Andric /// A pseudo model runner. We use it to store feature values when collecting 264af732203SDimitry Andric /// logs for the default policy, but never ask it to 'run'. 265af732203SDimitry Andric class NoInferenceModelRunner : public MLModelRunner { 266af732203SDimitry Andric public: 267af732203SDimitry Andric NoInferenceModelRunner(LLVMContext &Ctx) 268af732203SDimitry Andric : MLModelRunner(Ctx), Features(NumberOfFeatures) {} 269af732203SDimitry Andric void setFeature(FeatureIndex Index, int64_t Value) override { 270af732203SDimitry Andric Features[static_cast<int>(Index)] = Value; 271af732203SDimitry Andric } 272af732203SDimitry Andric 273af732203SDimitry Andric int64_t getFeature(int Index) const override { return Features[Index]; } 274af732203SDimitry Andric bool run() override { 275af732203SDimitry Andric llvm_unreachable("We shouldn't call run on this model runner."); 276af732203SDimitry Andric } 277af732203SDimitry Andric 278af732203SDimitry Andric private: 279af732203SDimitry Andric InlineFeatures Features; 280af732203SDimitry Andric }; 281af732203SDimitry Andric 282af732203SDimitry Andric /// ModelUnderTrainingRunner - training mode implementation. It uses TF C APIs 283af732203SDimitry Andric /// to dynamically load and evaluate a TF SavedModel 284af732203SDimitry Andric /// (https://www.tensorflow.org/guide/saved_model). Runtime performance is 285af732203SDimitry Andric /// sacrificed for ease of use while training. 286af732203SDimitry Andric class ModelUnderTrainingRunner final : public MLModelRunner { 287af732203SDimitry Andric public: 288af732203SDimitry Andric ModelUnderTrainingRunner(LLVMContext &Ctx, const std::string &ModelPath); 289af732203SDimitry Andric 290af732203SDimitry Andric bool run() override; 291af732203SDimitry Andric 292af732203SDimitry Andric // Disallows copy and assign. 293af732203SDimitry Andric ModelUnderTrainingRunner(const ModelUnderTrainingRunner &) = delete; 294af732203SDimitry Andric ModelUnderTrainingRunner & 295af732203SDimitry Andric operator=(const ModelUnderTrainingRunner &) = delete; 296af732203SDimitry Andric 297af732203SDimitry Andric void setFeature(FeatureIndex Index, int64_t Value) override; 298af732203SDimitry Andric int64_t getFeature(int Index) const override; 299af732203SDimitry Andric bool isValid() const { return !!Evaluator; } 300af732203SDimitry Andric 301af732203SDimitry Andric const std::vector<LoggedFeatureSpec> &outputLoggedFeatureSpecs() const { 302af732203SDimitry Andric return OutputSpecs; 303af732203SDimitry Andric } 304af732203SDimitry Andric 305af732203SDimitry Andric const Optional<TFModelEvaluator::EvaluationResult> & 306af732203SDimitry Andric lastEvaluationResult() const { 307af732203SDimitry Andric return LastEvaluationResult; 308af732203SDimitry Andric } 309af732203SDimitry Andric 310af732203SDimitry Andric private: 311af732203SDimitry Andric std::unique_ptr<TFModelEvaluator> Evaluator; 312af732203SDimitry Andric std::vector<LoggedFeatureSpec> OutputSpecs; 313af732203SDimitry Andric Optional<TFModelEvaluator::EvaluationResult> LastEvaluationResult; 314af732203SDimitry Andric 315af732203SDimitry Andric // The training framework needs some additional features. 316af732203SDimitry Andric const std::vector<TensorSpec> TrainingOnlyFeatures{ 317af732203SDimitry Andric TensorSpec::createSpec<int64_t>(TFFeedPrefix + "inlining_default", {1}), 318af732203SDimitry Andric TensorSpec::createSpec<float>(TFFeedPrefix + "discount", {1}), 319af732203SDimitry Andric TensorSpec::createSpec<float>(TFFeedPrefix + "reward", {1}), 320af732203SDimitry Andric TensorSpec::createSpec<int32_t>(TFFeedPrefix + "step_type", {1})}; 321af732203SDimitry Andric }; 322af732203SDimitry Andric } // namespace 323af732203SDimitry Andric 324af732203SDimitry Andric TrainingLogger::TrainingLogger(StringRef LogFileName, 325af732203SDimitry Andric const ModelUnderTrainingRunner *MUTR) 326af732203SDimitry Andric : LogFileName(LogFileName), MUTR(MUTR) { 327af732203SDimitry Andric // The first output is the inlining decision. 328af732203SDimitry Andric if (MUTR) 329af732203SDimitry Andric OutputCount = MUTR->outputLoggedFeatureSpecs().size(); 330af732203SDimitry Andric std::vector<LoggedFeatureSpec> FT; 331af732203SDimitry Andric 332af732203SDimitry Andric for (size_t I = 0; I < NumberOfFeatures; ++I) 333af732203SDimitry Andric FT.push_back( 334af732203SDimitry Andric {TensorSpec::createSpec<int64_t>(FeatureNameMap.at(I), {1}), None}); 335af732203SDimitry Andric if (MUTR && MUTR->outputLoggedFeatureSpecs().size() > 1) 336af732203SDimitry Andric append_range(FT, drop_begin(MUTR->outputLoggedFeatureSpecs())); 337af732203SDimitry Andric 338af732203SDimitry Andric DefaultDecisionPos = FT.size(); 339af732203SDimitry Andric FT.push_back( 340af732203SDimitry Andric {TensorSpec::createSpec<int64_t>(DefaultDecisionName, {1}), None}); 341af732203SDimitry Andric 342af732203SDimitry Andric DecisionPos = FT.size(); 343af732203SDimitry Andric FT.push_back({TensorSpec::createSpec<int64_t>(DecisionName, {1}), None}); 344af732203SDimitry Andric 345af732203SDimitry Andric L = std::make_unique<Logger>( 346af732203SDimitry Andric FT, TensorSpec::createSpec<int64_t>(RewardName, {1}), 347af732203SDimitry Andric InlineSizeEstimatorAnalysis::isEvaluatorRequested()); 348af732203SDimitry Andric } 349af732203SDimitry Andric 350af732203SDimitry Andric /// Log one inlining event. 351af732203SDimitry Andric void TrainingLogger::logInlineEvent(const InlineEvent &Event, 352af732203SDimitry Andric const MLModelRunner &ModelRunner) { 353af732203SDimitry Andric size_t CurrentFeature = 0; 354af732203SDimitry Andric for (; CurrentFeature < NumberOfFeatures; ++CurrentFeature) { 355af732203SDimitry Andric int64_t F = ModelRunner.getFeature(CurrentFeature); 356*5f7ddb14SDimitry Andric L->logInt64Value(CurrentFeature, &F); 357af732203SDimitry Andric } 358af732203SDimitry Andric 359af732203SDimitry Andric for (size_t I = 1; I < OutputCount; ++I) { 360af732203SDimitry Andric const auto &Result = *MUTR->lastEvaluationResult(); 361af732203SDimitry Andric const char *RawData = 362af732203SDimitry Andric reinterpret_cast<const char *>(Result.getUntypedTensorValue(I)); 363*5f7ddb14SDimitry Andric L->logSpecifiedTensorValue(CurrentFeature, RawData); 364af732203SDimitry Andric ++CurrentFeature; 365af732203SDimitry Andric } 366af732203SDimitry Andric 367af732203SDimitry Andric assert(CurrentFeature == DefaultDecisionPos); 368*5f7ddb14SDimitry Andric L->logInt64Value(DefaultDecisionPos, &Event.DefaultDecision); 369*5f7ddb14SDimitry Andric L->logInt64Value(DecisionPos, &Event.AdvisedDecision); 370af732203SDimitry Andric if (InlineSizeEstimatorAnalysis::isEvaluatorRequested()) 371*5f7ddb14SDimitry Andric L->logInt64Reward(Event.Reward); 372af732203SDimitry Andric 373af732203SDimitry Andric // For debugging / later use 374af732203SDimitry Andric Effects.push_back(Event.Effect); 375af732203SDimitry Andric } 376af732203SDimitry Andric 377af732203SDimitry Andric void TrainingLogger::print() { 378af732203SDimitry Andric std::error_code EC; 379af732203SDimitry Andric raw_fd_ostream OutFile(LogFileName, EC); 380af732203SDimitry Andric L->print(OutFile); 381af732203SDimitry Andric } 382af732203SDimitry Andric 383af732203SDimitry Andric DevelopmentModeMLInlineAdvisor::DevelopmentModeMLInlineAdvisor( 384af732203SDimitry Andric Module &M, ModuleAnalysisManager &MAM, 385af732203SDimitry Andric std::unique_ptr<MLModelRunner> ModelRunner, 386af732203SDimitry Andric std::function<bool(CallBase &)> GetDefaultAdvice, bool IsDoingInference, 387af732203SDimitry Andric std::unique_ptr<TrainingLogger> Logger) 388af732203SDimitry Andric : MLInlineAdvisor(M, MAM, std::move(ModelRunner)), 389af732203SDimitry Andric GetDefaultAdvice(GetDefaultAdvice), IsDoingInference(IsDoingInference), 390af732203SDimitry Andric Logger(std::move(Logger)), 391af732203SDimitry Andric InitialNativeSize(isLogging() ? getTotalSizeEstimate() : 0), 392af732203SDimitry Andric CurrentNativeSize(InitialNativeSize) { 393af732203SDimitry Andric // We cannot have the case of neither inference nor logging. 394af732203SDimitry Andric assert(IsDoingInference || isLogging()); 395af732203SDimitry Andric } 396af732203SDimitry Andric 397af732203SDimitry Andric DevelopmentModeMLInlineAdvisor::~DevelopmentModeMLInlineAdvisor() { 398af732203SDimitry Andric if (isLogging()) 399af732203SDimitry Andric Logger->print(); 400af732203SDimitry Andric } 401af732203SDimitry Andric 402af732203SDimitry Andric Optional<size_t> 403af732203SDimitry Andric DevelopmentModeMLInlineAdvisor::getNativeSizeEstimate(const Function &F) const { 404af732203SDimitry Andric if (!InlineSizeEstimatorAnalysis::isEvaluatorRequested()) 405af732203SDimitry Andric return None; 406af732203SDimitry Andric auto &R = 407af732203SDimitry Andric FAM.getResult<InlineSizeEstimatorAnalysis>(const_cast<Function &>(F)); 408af732203SDimitry Andric if (!R) { 409af732203SDimitry Andric F.getParent()->getContext().emitError( 410af732203SDimitry Andric "Native size estimator is not present."); 411af732203SDimitry Andric return 0; 412af732203SDimitry Andric } 413af732203SDimitry Andric return *R; 414af732203SDimitry Andric } 415af732203SDimitry Andric 416af732203SDimitry Andric std::unique_ptr<MLInlineAdvice> 417af732203SDimitry Andric DevelopmentModeMLInlineAdvisor::getMandatoryAdviceImpl(CallBase &CB) { 418af732203SDimitry Andric return std::make_unique<LoggingMLInlineAdvice>( 419af732203SDimitry Andric /*Advisor=*/this, 420af732203SDimitry Andric /*CB=*/CB, /*ORE=*/getCallerORE(CB), /*Recommendation=*/true, 421af732203SDimitry Andric /*Logger=*/*Logger, 422af732203SDimitry Andric /*CallerSizeEstimateBefore=*/getNativeSizeEstimate(*CB.getCaller()), 423af732203SDimitry Andric /*CalleeSizeEstimateBefore=*/ 424af732203SDimitry Andric getNativeSizeEstimate(*CB.getCalledFunction()), 425af732203SDimitry Andric /*DefaultDecision=*/true, /*Mandatory*/ true); 426af732203SDimitry Andric } 427af732203SDimitry Andric 428af732203SDimitry Andric std::unique_ptr<MLInlineAdvice> 429af732203SDimitry Andric DevelopmentModeMLInlineAdvisor::getAdviceFromModel( 430af732203SDimitry Andric CallBase &CB, OptimizationRemarkEmitter &ORE) { 431af732203SDimitry Andric if (IsDoingInference && !isLogging()) 432af732203SDimitry Andric return MLInlineAdvisor::getAdviceFromModel(CB, ORE); 433af732203SDimitry Andric 434af732203SDimitry Andric bool DefaultAdvice = GetDefaultAdvice(CB); 435af732203SDimitry Andric auto Recommendation = IsDoingInference ? ModelRunner->run() : DefaultAdvice; 436af732203SDimitry Andric return std::make_unique<LoggingMLInlineAdvice>( 437af732203SDimitry Andric /*Advisor=*/this, 438af732203SDimitry Andric /*CB=*/CB, /*ORE=*/ORE, /*Recommendation=*/Recommendation, 439af732203SDimitry Andric /*Logger=*/*Logger, 440af732203SDimitry Andric /*CallerSizeEstimateBefore=*/getNativeSizeEstimate(*CB.getCaller()), 441af732203SDimitry Andric /*CalleeSizeEstimateBefore=*/ 442af732203SDimitry Andric getNativeSizeEstimate(*CB.getCalledFunction()), 443af732203SDimitry Andric /*DefaultDecision=*/DefaultAdvice); 444af732203SDimitry Andric } 445af732203SDimitry Andric 446af732203SDimitry Andric size_t DevelopmentModeMLInlineAdvisor::getTotalSizeEstimate() { 447af732203SDimitry Andric if (!InlineSizeEstimatorAnalysis::isEvaluatorRequested()) 448af732203SDimitry Andric return 0; 449af732203SDimitry Andric size_t Ret = 0; 450af732203SDimitry Andric for (auto &F : M) { 451af732203SDimitry Andric if (F.isDeclaration()) 452af732203SDimitry Andric continue; 453af732203SDimitry Andric if (isFunctionDeleted(&F)) 454af732203SDimitry Andric continue; 455af732203SDimitry Andric Ret += *getNativeSizeEstimate(F); 456af732203SDimitry Andric } 457af732203SDimitry Andric return Ret; 458af732203SDimitry Andric } 459af732203SDimitry Andric 460af732203SDimitry Andric ModelUnderTrainingRunner::ModelUnderTrainingRunner(LLVMContext &Ctx, 461af732203SDimitry Andric const std::string &ModelPath) 462af732203SDimitry Andric : MLModelRunner(Ctx) { 463af732203SDimitry Andric std::vector<TensorSpec> InputSpecs; 464af732203SDimitry Andric for (size_t I = 0; I < NumberOfFeatures; ++I) 465af732203SDimitry Andric InputSpecs.push_back( 466af732203SDimitry Andric TensorSpec::createSpec<int64_t>(TFFeedPrefix + FeatureNameMap[I], {1})); 467af732203SDimitry Andric append_range(InputSpecs, TrainingOnlyFeatures); 468af732203SDimitry Andric if (auto MaybeOutSpecs = 469af732203SDimitry Andric loadOutputSpecs(Ctx, DecisionName, ModelPath, TFOutputSpecOverride)) 470af732203SDimitry Andric OutputSpecs = std::move(*MaybeOutSpecs); 471af732203SDimitry Andric else 472af732203SDimitry Andric return; 473af732203SDimitry Andric 474af732203SDimitry Andric Evaluator = std::make_unique<TFModelEvaluator>( 475af732203SDimitry Andric ModelPath, InputSpecs, [&](size_t I) { return OutputSpecs[I].Spec; }, 476af732203SDimitry Andric OutputSpecs.size()); 477af732203SDimitry Andric if (!Evaluator || !Evaluator->isValid()) { 478af732203SDimitry Andric Ctx.emitError("Failed to create inliner saved model evaluator"); 479af732203SDimitry Andric Evaluator.reset(); 480af732203SDimitry Andric return; 481af732203SDimitry Andric } 482af732203SDimitry Andric } 483af732203SDimitry Andric 484af732203SDimitry Andric bool ModelUnderTrainingRunner::run() { 485af732203SDimitry Andric LastEvaluationResult = Evaluator->evaluate(); 486af732203SDimitry Andric if (!LastEvaluationResult.hasValue()) { 487af732203SDimitry Andric Ctx.emitError("Error evaluating model."); 488af732203SDimitry Andric return false; 489af732203SDimitry Andric } 490af732203SDimitry Andric int64_t Decision = *LastEvaluationResult->getTensorValue<int64_t>(0); 491af732203SDimitry Andric return static_cast<bool>(Decision); 492af732203SDimitry Andric } 493af732203SDimitry Andric 494af732203SDimitry Andric int64_t ModelUnderTrainingRunner::getFeature(int Index) const { 495af732203SDimitry Andric return *Evaluator->getInput<int64_t>(Index); 496af732203SDimitry Andric } 497af732203SDimitry Andric 498af732203SDimitry Andric void ModelUnderTrainingRunner::setFeature(FeatureIndex Index, int64_t Value) { 499af732203SDimitry Andric size_t NumericIndex = static_cast<size_t>(Index); 500af732203SDimitry Andric *(Evaluator->getInput<int64_t>(NumericIndex)) = Value; 501af732203SDimitry Andric } 502af732203SDimitry Andric 503af732203SDimitry Andric std::unique_ptr<InlineAdvisor> llvm::getDevelopmentModeAdvisor( 504af732203SDimitry Andric Module &M, ModuleAnalysisManager &MAM, 505af732203SDimitry Andric std::function<bool(CallBase &)> GetDefaultAdvice) { 506af732203SDimitry Andric auto &Ctx = M.getContext(); 507af732203SDimitry Andric std::unique_ptr<MLModelRunner> Runner; 508af732203SDimitry Andric ModelUnderTrainingRunner *MUTRPtr = nullptr; 509af732203SDimitry Andric bool IsDoingInference = false; 510af732203SDimitry Andric if (TFModelUnderTrainingPath.empty()) 511af732203SDimitry Andric Runner.reset(new NoInferenceModelRunner(Ctx)); 512af732203SDimitry Andric else { 513af732203SDimitry Andric auto MUTR = std::make_unique<ModelUnderTrainingRunner>( 514af732203SDimitry Andric Ctx, TFModelUnderTrainingPath); 515af732203SDimitry Andric if (!MUTR || !MUTR->isValid()) { 516af732203SDimitry Andric Ctx.emitError("Could not load the policy model from the provided path"); 517af732203SDimitry Andric return nullptr; 518af732203SDimitry Andric } 519af732203SDimitry Andric IsDoingInference = true; 520af732203SDimitry Andric MUTRPtr = MUTR.get(); 521af732203SDimitry Andric Runner = std::move(MUTR); 522af732203SDimitry Andric } 523af732203SDimitry Andric std::unique_ptr<TrainingLogger> Logger; 524af732203SDimitry Andric if (!TrainingLog.empty()) 525af732203SDimitry Andric Logger = std::make_unique<TrainingLogger>(TrainingLog, MUTRPtr); 526af732203SDimitry Andric 527af732203SDimitry Andric return std::make_unique<DevelopmentModeMLInlineAdvisor>( 528af732203SDimitry Andric M, MAM, std::move(Runner), GetDefaultAdvice, IsDoingInference, 529af732203SDimitry Andric std::move(Logger)); 530af732203SDimitry Andric } 531af732203SDimitry Andric #endif // defined(LLVM_HAVE_TF_API) 532