LLVM 24.0.0git
TrainingLogger.h
Go to the documentation of this file.
1//===- TrainingLogger.h - mlgo feature/reward logging ----------*- C++ -*-===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8//
9// The design goals of the logger are:
10// - no dependencies that llvm doesn't already have.
11// - support streaming, so that we don't need to buffer data during compilation
12// - 0-decoding tensor values. Tensor values are potentially very large buffers
13// of scalars. Because of their potentially large size, avoiding
14// serialization/deserialization overhead is preferred.
15//
16// The simple logger produces an output of the form (each line item on its line)
17// - header: a json object describing the data that will follow.
18// - context: e.g. function name, for regalloc, or "default" for module-wide
19// optimizations like the inliner. This is the context to which the subsequent
20// data corresponds.
21// - observation number.
22// - tensor values - raw bytes of the tensors, in the order given in the header.
23// The values are in succession, i.e. no separator is found between successive
24// tensor values. At the end, there is a new line character.
25// - [score] - this is optional, and is present if it was present in the header.
26// Currently, for final rewards, we output "0" scores after each observation,
27// except for the last one.
28// <repeat>
29// The file should be read as binary, but the reason we use newlines is mostly
30// ease of debugging: the log can be opened in a text editor and, while tensor
31// values are inscrutable, at least the sequence of data can be easily observed.
32// Of course, the buffer of tensor values could contain '\n' bytes. A reader
33// should use the header information to know how much data to read for the
34// tensor values, and not use line information for that.
35//
36// An example reader, used for test, is available at
37// Analysis/models/log_reader.py
38//
39// Example:
40// {"features":[list of TensorSpecs], "score":<a tensor spec>}
41// {"context": "aFunction"}
42// {"observation": 0}
43// <bytes>
44// {"outcome": 0}
45// <bytes for the tensor corresponding to the "score" spec in the header>
46// {"observation": 1}
47// ...
48// {"context": "anotherFunction"}
49// {"observation": 0}
50// ...
51//
52
53#ifndef LLVM_ANALYSIS_UTILS_TRAININGLOGGER_H
54#define LLVM_ANALYSIS_UTILS_TRAININGLOGGER_H
55
56#include "llvm/Config/llvm-config.h"
58
59#include "llvm/ADT/StringMap.h"
60#include "llvm/ADT/Twine.h"
62#include "llvm/IR/LLVMContext.h"
63#include "llvm/Support/JSON.h"
64
65#include <memory>
66#include <optional>
67#include <vector>
68
69namespace llvm {
70
71/// Logging utility - given an ordered specification of features, and assuming
72/// a scalar reward, allow logging feature values and rewards.
73/// The assumption is that, for an event to be logged (i.e. a set of feature
74/// values and a reward), the user calls the log* API for each feature exactly
75/// once, providing the index matching the position in the feature spec list
76/// provided at construction. The example assumes the first feature's element
77/// type is float, the second is int64, and the reward is float:
78///
79/// event 0:
80/// logFloatValue(0, ...)
81/// logInt64Value(1, ...)
82/// ...
83/// logFloatReward(...)
84/// event 1:
85/// logFloatValue(0, ...)
86/// logInt64Value(1, ...)
87/// ...
88/// logFloatReward(...)
89///
90/// At the end, call print to generate the log.
91/// Alternatively, don't call logReward at the end of each event, just
92/// log{Float|Int32|Int64}FinalReward at the end.
93class Logger final {
94 std::unique_ptr<raw_ostream> OS;
95 const std::vector<TensorSpec> FeatureSpecs;
96 const TensorSpec RewardSpec;
97 const bool IncludeReward;
98 StringMap<size_t> ObservationIDs;
99 std::string CurrentContext;
100
101 void writeHeader(std::optional<TensorSpec> AdviceSpec);
102 void writeTensor(const TensorSpec &Spec, const char *RawData) {
103 OS->write(RawData, Spec.getTotalTensorBufferSize());
104 }
105 LLVM_ABI void logRewardImpl(const char *RawData);
106
107public:
108 /// Construct a Logger. If IncludeReward is false, then logReward or
109 /// logFinalReward shouldn't be called, and the reward feature won't be
110 /// printed out.
111 /// NOTE: the FeatureSpecs are expected to be in the same order (i.e. have
112 /// corresponding indices) with any MLModelRunner implementations
113 /// corresponding to the model being trained/logged.
114 LLVM_ABI Logger(std::unique_ptr<raw_ostream> OS,
115 const std::vector<TensorSpec> &FeatureSpecs,
116 const TensorSpec &RewardSpec, bool IncludeReward,
117 std::optional<TensorSpec> AdviceSpec = std::nullopt);
118
122 void flush() { OS->flush(); }
123
124 const std::string &currentContext() const { return CurrentContext; }
125
126 /// Check if there is at least an observation for `currentContext()`.
128 return hasAnyObservationForContext(CurrentContext);
129 }
130
131 /// Check if there is at least an observation for the context `Ctx`.
133 return ObservationIDs.contains(Ctx);
134 }
135
136 template <typename T> void logReward(T Value) {
137 logRewardImpl(reinterpret_cast<const char *>(&Value));
138 }
139
140 void logTensorValue(size_t FeatureID, const char *RawData) {
141 writeTensor(FeatureSpecs[FeatureID], RawData);
142 }
143};
144
145/// Context name for \p Name. An empty name gets a placeholder built from
146/// \p Number, braced so it does not clash with ordinary symbol names.
147inline std::string getLoggerContextName(StringRef Name, unsigned Number) {
148 if (!Name.empty())
149 return Name.str();
150 return ("{__unnamed_" + Twine(Number) + "}").str();
151}
152
153} // namespace llvm
154#endif // LLVM_ANALYSIS_UTILS_TRAININGLOGGER_H
This file defines the StringMap class.
#define LLVM_ABI
Definition Compiler.h:215
This file supports working with JSON data.
#define T
bool hasAnyObservationForContext(StringRef Ctx) const
Check if there is at least an observation for the context Ctx.
LLVM_ABI void startObservation()
bool hasObservationInProgress() const
Check if there is at least an observation for currentContext().
LLVM_ABI void switchContext(StringRef Name)
void logReward(T Value)
LLVM_ABI void endObservation()
void logTensorValue(size_t FeatureID, const char *RawData)
const std::string & currentContext() const
LLVM_ABI Logger(std::unique_ptr< raw_ostream > OS, const std::vector< TensorSpec > &FeatureSpecs, const TensorSpec &RewardSpec, bool IncludeReward, std::optional< TensorSpec > AdviceSpec=std::nullopt)
Construct a Logger.
StringMap - This is an unconventional map that is specialized for handling keys that are "strings",...
Definition StringMap.h:129
Represent a constant reference to a string, i.e.
Definition StringRef.h:56
Twine - A lightweight data structure for efficiently representing the concatenation of temporary valu...
Definition Twine.h:82
LLVM Value Representation.
Definition Value.h:75
This is an optimization pass for GlobalISel generic memory operations.
std::string getLoggerContextName(StringRef Name, unsigned Number)
Context name for Name.