Skip to content

Commit

Permalink
Silence Adams2019 Autoscheduler (halide#6854)
Browse files Browse the repository at this point in the history
* Make aslog() a proper ostream

* Ensure that all `dump()` calls take and use an ostream

* Progress Bar only draws as LogLevel >= 1

* clang-format

* Rework all aslog(0) statements

* Update ASLog.cpp

* syntax

* Update ASLog.cpp

* Revert fancy aslog stuff

* Update ASLog.h

* trigger buildbots
  • Loading branch information
steven-johnson authored and ardier committed Mar 3, 2024
1 parent 4ee71af commit 5ef8386
Show file tree
Hide file tree
Showing 11 changed files with 126 additions and 129 deletions.
7 changes: 7 additions & 0 deletions src/autoschedulers/adams2019/ASLog.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
// libHalide, so (despite the namespace) we are better off not
// including Halide.h, lest we reference something we won't have available

#include <cassert>
#include <cstdlib>
#include <iostream>
#include <utility>
Expand All @@ -28,6 +29,12 @@ class aslog {
return *this;
}

std::ostream &get_ostream() {
// It is an error to call this for an aslog() instance that cannot log.
assert(logging);
return std::cerr;
}

static int aslog_level();
};

Expand Down
77 changes: 41 additions & 36 deletions src/autoschedulers/adams2019/AutoSchedule.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -117,42 +117,45 @@ struct ProgressBar {
if (!draw_progress_bar) {
return;
}
auto &os = aslog(ProgressBarLogLevel).get_ostream();
counter++;
const int bits = 11;
if (counter & ((1 << bits) - 1)) {
return;
}
const int pos = (int)(progress * 78);
aslog(0) << "[";
os << "[";
for (int j = 0; j < 78; j++) {
if (j < pos) {
aslog(0) << ".";
os << ".";
} else if (j - 1 < pos) {
aslog(0) << "/-\\|"[(counter >> bits) % 4];
os << "/-\\|"[(counter >> bits) % 4];
} else {
aslog(0) << " ";
os << " ";
}
}
aslog(0) << "]";
os << "]";
for (int j = 0; j < 80; j++) {
aslog(0) << "\b";
os << "\b";
}
}

void clear() {
if (counter) {
auto &os = aslog(ProgressBarLogLevel).get_ostream();
for (int j = 0; j < 80; j++) {
aslog(0) << " ";
os << " ";
}
for (int j = 0; j < 80; j++) {
aslog(0) << "\b";
os << "\b";
}
}
}

private:
uint32_t counter = 0;
const bool draw_progress_bar = isatty(2);
static constexpr int ProgressBarLogLevel = 1;
const bool draw_progress_bar = isatty(2) && aslog::aslog_level() >= ProgressBarLogLevel;
};

// Get the HL_RANDOM_DROPOUT environment variable. Purpose of this is described above.
Expand Down Expand Up @@ -290,10 +293,6 @@ IntrusivePtr<State> optimal_schedule_pass(FunctionDAG &dag,

std::function<void(IntrusivePtr<State> &&)> enqueue_new_children =
[&](IntrusivePtr<State> &&s) {
// aslog(0) << "\n** Generated child: ";
// s->dump();
// s->calculate_cost(dag, params, nullptr, true);

// Each child should have one more decision made than its parent state.
internal_assert(s->num_decisions_made == s->parent->num_decisions_made + 1);

Expand Down Expand Up @@ -338,7 +337,7 @@ IntrusivePtr<State> optimal_schedule_pass(FunctionDAG &dag,
}

if ((int)pending.size() > beam_size * 10000) {
aslog(0) << "Warning: Huge number of states generated (" << pending.size() << ").\n";
aslog(1) << "*** Warning: Huge number of states generated (" << pending.size() << ").\n";
}

expanded = 0;
Expand Down Expand Up @@ -436,25 +435,26 @@ IntrusivePtr<State> optimal_schedule_pass(FunctionDAG &dag,
// The user has set HL_CYOS, and wants to navigate the
// search space manually. Discard everything in the queue
// except for the user-chosen option.
aslog(0) << "\n--------------------\n";
aslog(0) << "Select a schedule:\n";
std::cout << "\n--------------------\n";
std::cout << "Select a schedule:\n";
for (int choice_label = (int)q.size() - 1; choice_label >= 0; choice_label--) {
auto state = q[choice_label];
aslog(0) << "\n[" << choice_label << "]:\n";
state->dump();
state->calculate_cost(dag, params, cost_model, cache->options, memory_limit, true);
std::cout << "\n[" << choice_label << "]:\n";
state->dump(std::cout);
constexpr int verbosity_level = 0; // always
state->calculate_cost(dag, params, cost_model, cache->options, memory_limit, verbosity_level);
}
cost_model->evaluate_costs();

// Select next partial schedule to expand.
int selection = -1;
while (selection < 0 || selection >= (int)q.size()) {
aslog(0) << "\nEnter selection: ";
std::cout << "\nEnter selection: ";
std::cin >> selection;
}

auto selected = q[selection];
selected->dump();
selected->dump(std::cout);
q.clear();
q.emplace(std::move(selected));
}
Expand Down Expand Up @@ -508,11 +508,16 @@ IntrusivePtr<State> optimal_schedule(FunctionDAG &dag,

tick.clear();

if (aslog::aslog_level() == 0) {
aslog(0) << "Pass " << i << " of " << num_passes << ", cost: " << pass->cost << ", time (ms): " << milli << "\n";
} else {
aslog(0) << "Pass " << i << " result: ";
pass->dump();
switch (aslog::aslog_level()) {
case 0:
// Silence
break;
case 1:
aslog(1) << "Pass " << i << " of " << num_passes << ", cost: " << pass->cost << ", time (ms): " << milli << "\n";
break;
default:
aslog(2) << "Pass " << i << " result: ";
pass->dump(aslog(2).get_ostream());
}

if (i == 0 || pass->cost < best->cost) {
Expand All @@ -522,11 +527,11 @@ IntrusivePtr<State> optimal_schedule(FunctionDAG &dag,
}
}

aslog(0) << "Best cost: " << best->cost << "\n";
aslog(1) << "Best cost: " << best->cost << "\n";

if (options.cache_blocks) {
aslog(0) << "Cache (block) hits: " << cache.cache_hits << "\n";
aslog(0) << "Cache (block) misses: " << cache.cache_misses << "\n";
aslog(1) << "Cache (block) hits: " << cache.cache_hits << "\n";
aslog(1) << "Cache (block) misses: " << cache.cache_misses << "\n";
}

return best;
Expand All @@ -540,7 +545,7 @@ void generate_schedule(const std::vector<Function> &outputs,
const Target &target,
const MachineParams &params,
AutoSchedulerResults *auto_scheduler_results) {
aslog(0) << "generate_schedule for target=" << target.to_string() << "\n";
aslog(1) << "generate_schedule for target=" << target.to_string() << "\n";

// Start a timer
HALIDE_TIC;
Expand All @@ -554,7 +559,7 @@ void generate_schedule(const std::vector<Function> &outputs,
if (!seed_str.empty()) {
seed = atoi(seed_str.c_str());
}
aslog(1) << "Dropout seed = " << seed << "\n";
aslog(2) << "Dropout seed = " << seed << "\n";
std::mt19937 rng((uint32_t)seed);

// Get the beam size
Expand All @@ -576,8 +581,8 @@ void generate_schedule(const std::vector<Function> &outputs,

// Analyse the Halide algorithm and construct our abstract representation of it
FunctionDAG dag(outputs, params, target);
if (aslog::aslog_level() > 0) {
dag.dump();
if (aslog::aslog_level() >= 2) {
dag.dump(aslog(2).get_ostream());
}

// Construct a cost model to use to evaluate states. Currently we
Expand All @@ -602,14 +607,14 @@ void generate_schedule(const std::vector<Function> &outputs,
aslog(1) << "** Optimal schedule:\n";

// Just to get the debugging prints to fire
optimal->calculate_cost(dag, params, cost_model.get(), cache_options, memory_limit, aslog::aslog_level() > 0);
optimal->calculate_cost(dag, params, cost_model.get(), cache_options, memory_limit, /*verbosity_level*/ 1);

// Apply the schedules to the pipeline
optimal->apply_schedule(dag, params);

// Print out the schedule
if (aslog::aslog_level() > 0) {
optimal->dump();
if (aslog::aslog_level() >= 2) {
optimal->dump(aslog(2).get_ostream());
}

string schedule_file = get_env_variable("HL_SCHEDULE_FILE");
Expand Down
14 changes: 7 additions & 7 deletions src/autoschedulers/adams2019/DefaultCostModel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -232,18 +232,18 @@ float DefaultCostModel::backprop(const Runtime::Buffer<const float> &true_runtim
*(cost_ptrs(i)) = dst(i);
if (std::isnan(dst(i))) {
any_nans = true;
aslog(0) << "Prediction " << i << " is NaN. True runtime is " << true_runtimes(i) << "\n";
aslog(0) << "Checking pipeline features for NaNs...\n";
aslog(1) << "Prediction " << i << " is NaN. True runtime is " << true_runtimes(i) << "\n";
aslog(1) << "Checking pipeline features for NaNs...\n";
pipeline_feat_queue.for_each_value([&](float f) { if (std::isnan(f)) abort(); });
aslog(0) << "None found\n";
aslog(0) << "Checking schedule features for NaNs...\n";
aslog(1) << "None found\n";
aslog(1) << "Checking schedule features for NaNs...\n";
schedule_feat_queue.for_each_value([&](float f) { if (std::isnan(f)) abort(); });
aslog(0) << "None found\n";
aslog(0) << "Checking network weights for NaNs...\n";
aslog(1) << "None found\n";
aslog(1) << "Checking network weights for NaNs...\n";
weights.for_each_buffer([&](const Runtime::Buffer<float> &buf) {
buf.for_each_value([&](float f) { if (std::isnan(f)) abort(); });
});
aslog(0) << "None found\n";
aslog(1) << "None found\n";
}
internal_assert(true_runtimes(i) > 0);
}
Expand Down
14 changes: 2 additions & 12 deletions src/autoschedulers/adams2019/Featurization.h
Original file line number Diff line number Diff line change
Expand Up @@ -99,8 +99,7 @@ struct PipelineFeatures {
// Each row sums to 1 or 0. Each column sums to 1. f(z, y, x, 4)
int slice_accesses[(int)AccessType::NumAccessTypes][(int)ScalarType::NumScalarTypes] = {};

template<typename OS>
void dump(OS &os) const {
void dump(std::ostream &os) const {
for (int i = 0; i < (int)ScalarType::NumScalarTypes; i++) {
const char *type_names[] = {"Bool", "UInt8", "UInt16", "UInt32", "UInt64", "Float", "Double"};
// Skip printing for types not used
Expand Down Expand Up @@ -157,10 +156,6 @@ struct PipelineFeatures {
<< slice_accesses[3][i] << "\n";
}
}
void dump() const {
auto os = aslog(0);
dump(os);
}
};

// The schedule-dependent portion of the featurization of a stage
Expand Down Expand Up @@ -314,8 +309,7 @@ struct ScheduleFeatures {
double working_set_at_realization = 0;
double working_set_at_root = 0;

template<typename OS>
void dump(OS &os) const {
void dump(std::ostream &os) const {
os << " num_realizations: " << num_realizations << "\n"
<< " num_productions: " << num_productions << "\n"
<< " points_computed_per_realization: " << points_computed_per_realization << "\n"
Expand Down Expand Up @@ -356,10 +350,6 @@ struct ScheduleFeatures {
<< " working_set_at_realization: " << working_set_at_realization << "\n"
<< " working_set_at_root: " << working_set_at_root << "\n";
}
void dump() const {
auto os = aslog(0);
dump(os);
}

bool equal(const ScheduleFeatures &other) const {
const size_t n_features = ScheduleFeatures::num_features();
Expand Down
43 changes: 17 additions & 26 deletions src/autoschedulers/adams2019/FunctionDAG.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -307,42 +307,44 @@ class Featurizer : public IRVisitor {

} // namespace

void LoadJacobian::dump(const char *prefix) const {
void LoadJacobian::dump(std::ostream &os, const char *prefix) const {
if (count() > 1) {
aslog(0) << prefix << count() << " x\n";
os << prefix << count() << " x\n";
}
for (size_t i = 0; i < producer_storage_dims(); i++) {
aslog(0) << prefix << " [";
os << prefix << " [";

for (size_t j = 0; j < consumer_loop_dims(); j++) {
const auto &c = (*this)(i, j);
if (!c.exists) {
aslog(0) << " _ ";
os << " _ ";
} else if (c.denominator == 1) {
aslog(0) << " " << c.numerator << " ";
os << " " << c.numerator << " ";
} else {
aslog(0) << c.numerator << "/" << c.denominator << " ";
os << c.numerator << "/" << c.denominator << " ";
}
}
aslog(0) << "]\n";
os << "]\n";
}
aslog(0) << "\n";
os << "\n";
}

void BoundContents::validate() const {
for (int i = 0; i < layout->total_size; i++) {
auto p = data()[i];
if (p.max() < p.min()) {
aslog(0) << "Bad bounds object:\n";
std::ostringstream err;
err << "Bad bounds object:\n";
for (int j = 0; j < layout->total_size; j++) {
if (i == j) {
aslog(0) << "=> ";
err << "=> ";
} else {
aslog(0) << " ";
err << " ";
}
aslog(0) << j << ": " << data()[j].min() << ", " << data()[j].max() << "\n";
err << j << ": " << data()[j].min() << ", " << data()[j].max() << "\n";
}
internal_error << "Aborting";
err << "Aborting";
internal_error << err.str();
}
}
}
Expand Down Expand Up @@ -1039,8 +1041,7 @@ void FunctionDAG::featurize() {
}
}

template<typename OS>
void FunctionDAG::dump_internal(OS &os) const {
void FunctionDAG::dump(std::ostream &os) const {
for (const Node &n : nodes) {
os << "Node: " << n.func.name() << "\n"
<< " Symbolic region required: \n";
Expand Down Expand Up @@ -1076,21 +1077,11 @@ void FunctionDAG::dump_internal(OS &os) const {

os << " Load Jacobians:\n";
for (const auto &jac : e.load_jacobians) {
jac.dump(" ");
jac.dump(os, " ");
}
}
}

void FunctionDAG::dump() const {
auto os = aslog(0);
dump_internal(os);
}

std::ostream &FunctionDAG::dump(std::ostream &os) const {
dump_internal(os);
return os;
}

} // namespace Autoscheduler
} // namespace Internal
} // namespace Halide
8 changes: 2 additions & 6 deletions src/autoschedulers/adams2019/FunctionDAG.h
Original file line number Diff line number Diff line change
Expand Up @@ -205,7 +205,7 @@ class LoadJacobian {
return result;
}

void dump(const char *prefix) const;
void dump(std::ostream &os, const char *prefix) const;
};

// Classes to represent a concrete set of bounds for a Func. A Span is
Expand Down Expand Up @@ -565,16 +565,12 @@ struct FunctionDAG {
// analysis. This is done once up-front before the tree search.
FunctionDAG(const vector<Function> &outputs, const MachineParams &params, const Target &target);

void dump() const;
std::ostream &dump(std::ostream &os) const;
void dump(std::ostream &os) const;

private:
// Compute the featurization for the entire DAG
void featurize();

template<typename OS>
void dump_internal(OS &os) const;

public:
// This class uses a lot of internal pointers, so we'll make it uncopyable/unmovable.
FunctionDAG(const FunctionDAG &other) = delete;
Expand Down
Loading

0 comments on commit 5ef8386

Please sign in to comment.