• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

daisytuner / docc / 31007085647

05 Aug 2026 12:46PM UTC coverage: 65.113%. First build
31007085647

Pull #938

github

web-flow
Merge f25ad413b into 7d5b198bd
Pull Request #938: Better compile stats that supported nested timings

61 of 277 new or added lines in 9 files covered. (22.02%)

46352 of 71187 relevant lines covered (65.11%)

717.35 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.0
/sdfg/src/passes/statistics.cpp
1
#include "sdfg/passes/statistics.h"
2

3
#include <algorithm>
4
#include <cstdint>
5
#include <functional>
6
#include <iostream>
7
#include <memory>
8
#include <sstream>
9
#include <string>
10
#include <tuple>
11
#include <utility>
12
#include <vector>
13

14
#include "sdfg/helpers/helpers.h"
15
#include "sdfg/passes/expansion/lib_node_expander.h"
16

17
namespace sdfg {
18
namespace passes {
19

20
constexpr bool LOG_SCOPE_ENTRY_EXIT = false;
21

22

NEW
23
HierarchicalStatistics::Node* HierarchicalStatistics::enter_scope(const std::string& name, const std::string& scope_type) {
×
NEW
24
    auto node = std::make_unique<Node>();
×
NEW
25
    node->scope_type = scope_type;
×
NEW
26
    node->name = name;
×
NEW
27
    node->parent = current_;
×
NEW
28
    node->enter_time = std::chrono::high_resolution_clock::now();
×
29

NEW
30
    if (LOG_SCOPE_ENTRY_EXIT) {
×
NEW
31
        DEBUG_PRINTLN("Started " << scope_type << " '" << name);
×
32
    }
×
33

NEW
34
    Node* raw = node.get();
×
NEW
35
    if (current_ == nullptr) {
×
NEW
36
        roots_.push_back(std::move(node));
×
37
    } else {
×
NEW
38
        current_->children.push_back(std::move(node));
×
39
    }
×
NEW
40
    current_ = raw;
×
NEW
41
    return raw;
×
42
}
×
43

NEW
44
void HierarchicalStatistics::exit_scope() {
×
NEW
45
    if (current_ == nullptr) {
×
NEW
46
        return;
×
47
    }
×
NEW
48
    current_->exit_time = std::chrono::high_resolution_clock::now();
×
49

NEW
50
    auto duration =
×
NEW
51
        std::chrono::duration_cast<std::chrono::milliseconds>(current_->exit_time - current_->enter_time).count();
×
52

NEW
53
    if (LOG_SCOPE_ENTRY_EXIT) {
×
NEW
54
        DEBUG_PRINTLN("Finished " << current_->scope_type << " '" << current_->name << "' in " << duration << " ms");
×
55
    }
×
56

NEW
57
    current_ = current_->parent;
×
NEW
58
}
×
59

NEW
60
void HierarchicalStatistics::set_metric(const std::string& key, uint64_t value) {
×
NEW
61
    if (current_ == nullptr) {
×
NEW
62
        return;
×
63
    }
×
NEW
64
    current_->metrics[key] = value;
×
65
}
×
66

NEW
67
void HierarchicalStatistics::add_metric(const std::string& key, uint64_t value) {
×
NEW
68
    if (current_ == nullptr) {
×
NEW
69
        return;
×
70
    }
×
NEW
71
    current_->metrics[key] += value;
×
72
}
×
73

NEW
74
void HierarchicalStatistics::clear() {
×
NEW
75
    roots_.clear();
×
NEW
76
    current_ = nullptr;
×
77
}
×
78

NEW
79
int statistics_mode_env() {
×
NEW
80
    const char* val = std::getenv(DOCC_STATISTICS_ENV);
×
NEW
81
    if (val == nullptr) {
×
NEW
82
        return 0;
×
83
    }
×
NEW
84
    return std::stoi(val);
×
NEW
85
}
×
86

NEW
87
uint64_t HierarchicalStatistics::Node::duration_ms() const {
×
NEW
88
    return std::chrono::duration_cast<std::chrono::milliseconds>(this->exit_time - this->enter_time).count();
×
NEW
89
}
×
90

NEW
91
void HierarchicalStatistics::print_node_self(std::ostream& stream, const HierarchicalStatistics::Node& node, int depth) {
×
NEW
92
    std::string indent(2 * (depth + 1), ' ');
×
NEW
93
    stream << indent << node.scope_type << " " << node.name << "  " << node.duration_ms() << " ms  ";
×
NEW
94
    if (!node.metrics.empty()) {
×
NEW
95
        std::vector<std::pair<std::string, uint64_t>> metrics(node.metrics.begin(), node.metrics.end());
×
NEW
96
        std::sort(metrics.begin(), metrics.end(), [](const auto& a, const auto& b) { return a.first < b.first; });
×
NEW
97
        stream << "  [";
×
NEW
98
        for (size_t i = 0; i < metrics.size(); ++i) {
×
NEW
99
            if (i > 0) {
×
NEW
100
                stream << ", ";
×
NEW
101
            }
×
NEW
102
            stream << metrics[i].first << "=" << metrics[i].second;
×
103
        }
×
NEW
104
        stream << "]";
×
105
    }
×
NEW
106
    stream << std::endl;
×
NEW
107
}
×
108

NEW
109
HierarchicalStatistics::RemapAction HierarchicalStatistics::custom_print_node(std::ostream& out, const Node&, int depth) {
×
NEW
110
    return RemapAction::Proceed;
×
NEW
111
}
×
112

NEW
113
void HierarchicalStatistics::print_node(std::ostream& stream, const HierarchicalStatistics::Node& node, int depth) {
×
NEW
114
    bool descend_children = true;
×
NEW
115
    RemapAction action = custom_print_node(stream, node, depth);
×
NEW
116
    if (action == RemapAction::Skip) {
×
NEW
117
        return;
×
NEW
118
    } else if (action == RemapAction::NoDescend) {
×
NEW
119
        descend_children = false;
×
120
    }
×
121

NEW
122
    print_node_self(stream, node, depth);
×
NEW
123
    if (descend_children) {
×
NEW
124
        for (const auto& child : node.children) {
×
NEW
125
            print_node(stream, *child, depth + 1);
×
126
        }
×
127
    }
×
NEW
128
}
×
129

NEW
130
std::string HierarchicalStatistics::report(const std::string& title) {
×
NEW
131
    if (roots_.empty()) {
×
NEW
132
        return "";
×
NEW
133
    }
×
NEW
134
    std::stringstream stream;
×
NEW
135
    stream << title << std::endl;
×
NEW
136
    for (const auto& root : roots_) {
×
NEW
137
        print_node(stream, *root, 0);
×
NEW
138
    }
×
139
    return stream.str();
×
140
}
×
141

142
bool CompileStatistics::enabled_ = false;
143

NEW
144
std::string CompileStatistics::summary() { return report(ReportLevel::SummarizePipelineContentsAndAnalysis); }
×
145

NEW
146
HierarchicalStatistics::RemapAction CompileStatistics::custom_print_node(std::ostream& out, const Node& node, int depth) {
×
NEW
147
    auto level = report_detail_;
×
NEW
148
    if (node.scope_type == PIPELINE_SCOPE) {
×
NEW
149
        if (level == ReportLevel::SummarizePipelineContentsAndAnalysis) {
×
NEW
150
            print_node_self(out, node, depth);
×
NEW
151
            summarize_pipeline_passes(out, node, depth);
×
NEW
152
            return RemapAction::Skip;
×
NEW
153
        } else if (level == ReportLevel::PipelineTotalsOnly) {
×
NEW
154
            return RemapAction::NoDescend;
×
NEW
155
        } else {
×
NEW
156
            return RemapAction::Proceed;
×
NEW
157
        }
×
NEW
158
    } else if (node.scope_type == ANALYSIS_SCOPE || node.scope_type == PASS_SCOPE) {
×
NEW
159
        if (level != ReportLevel::All) {
×
NEW
160
            return RemapAction::NoDescend;
×
NEW
161
        } else {
×
NEW
162
            return RemapAction::Proceed;
×
NEW
163
        }
×
NEW
164
    } else if (node.scope_type == ANALYSIS_MGR_SCOPE && depth < 2 && level != ReportLevel::All) {
×
NEW
165
        return RemapAction::Skip;
×
166
    }
×
NEW
167
    return RemapAction::Proceed;
×
168
}
×
169

NEW
170
void CompileStatistics::summarize_pipeline_passes(std::ostream& out, const HierarchicalStatistics::Node& node, int depth) {
×
171
    // Aggregate direct children only: passes by name, everything else under "other" by scope type.
NEW
172
    std::unordered_map<std::string, std::pair<uint64_t, uint64_t>> passes;
×
NEW
173
    std::vector<std::string> pass_order;
×
NEW
174
    std::unordered_map<std::string, std::pair<uint64_t, uint64_t>> others;
×
175

NEW
176
    for (const auto& child : node.children) {
×
NEW
177
        if (child->scope_type == PASS_SCOPE) {
×
NEW
178
            auto [it, inserted] = passes.try_emplace(child->name, 0, 0);
×
NEW
179
            if (inserted) {
×
NEW
180
                pass_order.push_back(child->name);
×
NEW
181
            }
×
NEW
182
            it->second.first += 1;
×
NEW
183
            it->second.second += child->duration_ms();
×
NEW
184
        } else {
×
NEW
185
            auto& bucket = others[child->scope_type];
×
NEW
186
            bucket.first += 1;
×
NEW
187
            bucket.second += child->duration_ms();
×
NEW
188
        }
×
189
    }
×
190

191
    auto compare_data_fn = [](const std::tuple<std::string, uint64_t, uint64_t>& a,
×
192
                              const std::tuple<std::string, uint64_t, uint64_t>& b) {
×
193
        auto [a_name, a_count, a_milliseconds] = a;
×
194
        auto [b_name, b_count, b_milliseconds] = b;
×
195
        return a_milliseconds > b_milliseconds || (a_milliseconds == b_milliseconds && a_count > b_count) ||
×
196
               (a_milliseconds == b_milliseconds && a_count == b_count && a_name < b_name);
×
197
    };
×
198

NEW
199
    std::string indent(2 * (depth + 2), ' ');
×
200

201
    // Passes are reported in first-encounter order, matching the pipeline execution order.
NEW
202
    for (const auto& name : pass_order) {
×
NEW
203
        const auto& entry = passes[name];
×
NEW
204
        out << indent << entry.first << " x " << name << "  " << entry.second << " ms" << std::endl;
×
205
    }
×
206

NEW
207
    std::vector<std::tuple<std::string, uint64_t, uint64_t>> other_data;
×
NEW
208
    for (const auto& [scope_type, entry] : others) {
×
NEW
209
        other_data.push_back({scope_type, entry.first, entry.second});
×
NEW
210
    }
×
NEW
211
    std::sort(other_data.begin(), other_data.end(), compare_data_fn);
×
NEW
212
    for (const auto& [scope_type, count, milliseconds] : other_data) {
×
NEW
213
        out << indent << "Other: " << milliseconds << " ms  " << count << "  " << scope_type << std::endl;
×
214
    }
×
NEW
215
}
×
216

NEW
217
std::string CompileStatistics::report(ReportLevel detail) {
×
NEW
218
    this->report_detail_ = detail;
×
219

NEW
220
    return HierarchicalStatistics::report("DOCC Statistics:");
×
221
}
×
222

223

224
void CodegenStatistics::add_codegen(const std::string& name, uint64_t milliseconds) {
×
225
    if (!count_.contains(name)) {
×
226
        count_.insert({name, 1});
×
227
    } else {
×
228
        count_[name]++;
×
229
    }
×
230
    if (!time_.contains(name)) {
×
231
        time_.insert({name, milliseconds});
×
232
    } else {
×
233
        time_[name] += milliseconds;
×
234
    }
×
235
}
×
236

237
std::string CodegenStatistics::summary() {
×
238
    if (count_.empty()) {
×
239
        return "";
×
240
    }
×
241

242
    auto compare_data_fn = [](const std::tuple<std::string, uint64_t, uint64_t>& a,
×
243
                              const std::tuple<std::string, uint64_t, uint64_t>& b) {
×
244
        auto [a_name, a_count, a_milliseconds] = a;
×
245
        auto [b_name, b_count, b_milliseconds] = b;
×
246
        return a_milliseconds > b_milliseconds || (a_milliseconds == b_milliseconds && a_count > b_count) ||
×
247
               (a_milliseconds == b_milliseconds && a_count == b_count && a_name < b_name);
×
248
    };
×
249
    std::stringstream stream;
×
250
    stream << "Codegen Statistics:" << std::endl;
×
251

252
    std::vector<std::tuple<std::string, uint64_t, uint64_t>> data;
×
253
    uint64_t time_sum = 0;
×
254
    for (auto [name, count] : count_) {
×
255
        if (time_.contains(name)) {
×
256
            auto milliseconds = time_[name];
×
257
            data.push_back({name, count, milliseconds});
×
258
            time_sum += milliseconds;
×
259
        }
×
260
    }
×
261
    std::sort(data.begin(), data.end(), compare_data_fn);
×
262

263
    if (!data.empty()) {
×
264
        stream << "  Codegen: " << time_sum << " ms" << std::endl;
×
265
        for (auto [name, count, milliseconds] : data) {
×
266
            stream << "    " << milliseconds << " ms  " << count << "  " << name << std::endl;
×
267
        }
×
268
    }
×
269

270
    return stream.str();
×
271
}
×
272

273
} // namespace passes
274
} // namespace sdfg
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE TRIAL · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc