Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
48 changes: 45 additions & 3 deletions src/Profiling.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -356,9 +356,17 @@ class InjectCounters : public IRMutator {
ParallelLoops,
ParallelTasks,
PointsRequiredAtRoot,
PointsComputed };

static constexpr int num_counters = PointsComputed + 1;
PointsComputed,
ScalarLoads,
VectorLoads,
Gathers,
BytesLoaded,
ScalarStores,
VectorStores,
Scatters,
BytesStored };

static constexpr int num_counters = BytesStored + 1;

struct Counters {

Expand Down Expand Up @@ -706,6 +714,10 @@ class InjectCounters : public IRMutator {
bool is_real_data_buffer(const Store *op) const {
return op->param.defined() || is_func(names.prefix(op->name));
}
bool is_real_data_buffer(const Load *op) const {
return op->param.defined() || op->image.defined() ||
is_func(names.prefix(op->name));
}

Stmt visit(const Store *op) override {
if (is_real_data_buffer(op)) {
Expand All @@ -718,6 +730,17 @@ class InjectCounters : public IRMutator {
names.id_for_name(f);
Counters &c = counters[id];
int lanes = op->value.type().lanes();
// Classify the store by its index: scalar, unit-stride vector, or
// scatter. Drives the vectorization performance warnings.
if (op->index.type().is_scalar()) {
c.count(ScalarStores);
} else if (const Ramp *r = op->index.as<Ramp>();
r && is_const_one(r->stride)) {
c.count(VectorStores);
} else {
c.count(Scatters);
}
c.count(BytesStored, make_const(UInt(64), op->value.type().bytes() * lanes));
// Only the pure def (stage 0) contributes to "points computed";
// update-def stores are a separate kind of work and shouldn't
// show up as recompute. For Tuple-valued Funcs each output
Expand All @@ -737,6 +760,25 @@ class InjectCounters : public IRMutator {
return IRMutator::visit(op);
}

Expr visit(const Load *op) override {
// We bill these to the Func we're producing, not the Func being
// loaded. These counters are about what kinds of loads we do while
// computing a given Func.
if (producer_id >= 0 && is_real_data_buffer(op)) {
Counters &c = counters[producer_id];
if (op->index.type().is_scalar()) {
c.count(ScalarLoads);
} else if (const Ramp *r = op->index.as<Ramp>();
r && is_const_one(r->stride)) {
c.count(VectorLoads);
} else {
c.count(Gathers);
}
c.count(BytesLoaded, make_const(UInt(64), op->type.bytes() * op->type.lanes()));
}
return IRMutator::visit(op);
}

Stmt visit(const ProducerConsumer *op) override {
if (op->is_producer) {
// One entry per producer node, parented to the surrounding
Expand Down
11 changes: 11 additions & 0 deletions src/runtime/HalideRuntime.h
Original file line number Diff line number Diff line change
Expand Up @@ -2097,6 +2097,17 @@ struct HALIDE_ATTRIBUTE_ALIGN(8) halide_profiler_func_stats {
* stores keeps update definitions from being conflated as
* "recompute". */
uint64_t points_computed;

/** The number of loads of various kinds done while computing this Func,
* classified by the index expression: scalar (a scalar index),
* vector (a unit-stride ramp), or gather (anything else). bytes_loaded
* is the total number of bytes loaded across all three. */
uint64_t scalar_loads, vector_loads, gathers, bytes_loaded;

/** The number of stores of various kinds done while computing this Func,
* classified the same way (scalar / unit-stride vector / scatter), plus
* the total bytes stored. */
uint64_t scalar_stores, vector_stores, scatters, bytes_stored;
};

/** Per-pipeline state tracked by the sampling profiler. These exist
Expand Down
Loading
Loading