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

daisytuner / docc / 23641798779

27 Mar 2026 08:40AM UTC coverage: 64.449% (+0.3%) from 64.115%
23641798779

Pull #503

github

web-flow
Merge c7d52d496 into c252af595
Pull Request #503: Added pass and pipeline statistics

28 of 300 new or added lines in 20 files covered. (9.33%)

503 existing lines in 16 files now uncovered.

27015 of 41917 relevant lines covered (64.45%)

406.1 hits per line

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

92.37
/opt/src/passes/normalization/stride_minimization.cpp
1
#include "sdfg/passes/normalization/stride_minimization.h"
2

3
#include <sdfg/analysis/loop_analysis.h>
4
#include <sdfg/transformations/loop_interchange.h>
5

6
namespace sdfg {
7
namespace passes {
8
namespace normalization {
9

10
StrideMinimization::StrideMinimization() : Pass() {};
14✔
11

12
bool StrideMinimization::is_admissible(
13
    std::vector<std::string>& current, std::vector<std::string>& target, std::unordered_set<std::string>& allowed_swaps
14
) {
273✔
15
    std::vector<size_t> permutation_indices;
273✔
16
    for (auto& p : current) {
821✔
17
        auto it = std::find(target.begin(), target.end(), p);
821✔
18
        permutation_indices.push_back(std::distance(target.begin(), it));
821✔
19
    }
821✔
20
    for (size_t i = 0; i < permutation_indices.size(); i++) {
608✔
21
        for (size_t j = 0; j < permutation_indices.size() - i - 1; j++) {
921✔
22
            if (permutation_indices[j] > permutation_indices[j + 1]) {
586✔
23
                std::string swap = target[permutation_indices[j]] + "_" + target[permutation_indices[j + 1]];
294✔
24
                if (allowed_swaps.find(swap) == allowed_swaps.end()) {
294✔
25
                    return false;
154✔
26
                }
154✔
27
                size_t tmp = permutation_indices[j];
140✔
28
                permutation_indices[j] = permutation_indices[j + 1];
140✔
29
                permutation_indices[j + 1] = tmp;
140✔
30
            }
140✔
31
        }
586✔
32
    }
489✔
33

34
    return true;
119✔
35
};
273✔
36

37
std::unordered_set<std::string> StrideMinimization::allowed_swaps(
38
    builder::StructuredSDFGBuilder& builder,
39
    analysis::AnalysisManager& analysis_manager,
40
    std::vector<structured_control_flow::ControlFlowNode*>& nested_loops
41
) {
60✔
42
    std::unordered_set<std::string> allowed_swaps;
60✔
43
    for (size_t i = 0; i < nested_loops.size() - 1; i++) {
147✔
44
        if (dynamic_cast<structured_control_flow::While*>(nested_loops.at(i)) ||
87✔
45
            dynamic_cast<structured_control_flow::While*>(nested_loops.at(i + 1))) {
87✔
46
            continue;
×
47
        } else {
87✔
48
            auto first_loop = dynamic_cast<structured_control_flow::StructuredLoop*>(nested_loops.at(i));
87✔
49
            auto second_loop = dynamic_cast<structured_control_flow::StructuredLoop*>(nested_loops.at(i + 1));
87✔
50
            transformations::LoopInterchange loop_interchange(*first_loop, *second_loop);
87✔
51
            if (loop_interchange.can_be_applied(builder, analysis_manager)) {
87✔
52
                allowed_swaps.insert(first_loop->indvar()->get_name() + "_" + second_loop->indvar()->get_name());
56✔
53
            }
56✔
54
        }
87✔
55
    }
87✔
56
    return allowed_swaps;
60✔
57
};
60✔
58

59
std::pair<bool, std::vector<std::string>> StrideMinimization::can_be_applied(
60
    builder::StructuredSDFGBuilder& builder,
61
    analysis::AnalysisManager& analysis_manager,
62
    std::vector<structured_control_flow::ControlFlowNode*>& nested_loops
63
) {
77✔
64
    if (nested_loops.size() < 2) {
77✔
65
        return {false, {}};
17✔
66
    }
17✔
67

68
    size_t while_loops = 0;
60✔
69
    std::vector<std::string> permutation;
60✔
70
    for (auto& loop : nested_loops) {
147✔
71
        if (!dynamic_cast<structured_control_flow::StructuredLoop*>(loop)) {
147✔
72
            permutation.push_back("WHILE_" + std::to_string(while_loops++));
×
73
        } else {
147✔
74
            auto for_loop = dynamic_cast<structured_control_flow::StructuredLoop*>(loop);
147✔
75
            permutation.push_back(for_loop->indvar()->get_name());
147✔
76
        }
147✔
77
    }
147✔
78

79
    auto admissible_swaps = allowed_swaps(builder, analysis_manager, nested_loops);
60✔
80

81
    // Collect all memory accesses of body
82
    structured_control_flow::ControlFlowNode* nested_root = nullptr;
60✔
83
    if (auto for_loop = dynamic_cast<structured_control_flow::StructuredLoop*>(nested_loops.at(0))) {
60✔
84
        nested_root = &for_loop->root();
60✔
85
    } else if (auto while_loop = dynamic_cast<structured_control_flow::While*>(nested_loops.at(0))) {
60✔
86
        nested_root = &while_loop->root();
×
87
    } else {
×
88
        assert(false);
×
89
    }
×
90
    auto& all_users = analysis_manager.get<analysis::Users>();
60✔
91
    analysis::UsersView users(all_users, *nested_root);
60✔
92

93
    // Collect all memory accesses
94
    std::vector<data_flow::Subset> subsets;
60✔
95
    for (auto& use : users.reads()) {
1,297✔
96
        auto element = use->element();
1,297✔
97
        if (!dynamic_cast<data_flow::AccessNode*>(element)) {
1,297✔
98
            continue;
1,007✔
99
        }
1,007✔
100
        auto access = dynamic_cast<data_flow::AccessNode*>(element);
290✔
101
        auto& graph = access->get_parent();
290✔
102
        for (auto& oedge : graph.out_edges(*access)) {
315✔
103
            auto& subset = oedge.subset();
315✔
104

105
            // Filter the subset for only dimensions using the permutation
106
            data_flow::Subset filtered_subset;
315✔
107
            for (auto& dim : subset) {
426✔
108
                for (auto& perm : permutation) {
861✔
109
                    if (symbolic::uses(dim, perm)) {
861✔
110
                        filtered_subset.push_back(dim);
314✔
111
                        break;
314✔
112
                    }
314✔
113
                }
861✔
114
            }
426✔
115
            if (filtered_subset.empty()) {
315✔
116
                continue;
119✔
117
            }
119✔
118

119
            subsets.push_back(filtered_subset);
196✔
120
        }
196✔
121
    }
290✔
122
    for (auto& use : users.writes()) {
432✔
123
        auto element = use->element();
432✔
124
        if (!dynamic_cast<data_flow::AccessNode*>(element)) {
432✔
125
            continue;
278✔
126
        }
278✔
127
        auto access = dynamic_cast<data_flow::AccessNode*>(element);
154✔
128
        auto& graph = access->get_parent();
154✔
129
        for (auto& iedge : graph.in_edges(*access)) {
154✔
130
            auto& subset = iedge.subset();
154✔
131

132
            // Filter the subset for only dimensions using the permutation
133
            data_flow::Subset filtered_subset;
154✔
134
            for (auto& dim : subset) {
174✔
135
                for (auto& perm : permutation) {
345✔
136
                    if (symbolic::uses(dim, perm)) {
345✔
137
                        filtered_subset.push_back(dim);
126✔
138
                        break;
126✔
139
                    }
126✔
140
                }
345✔
141
            }
174✔
142
            if (filtered_subset.empty()) {
154✔
143
                continue;
73✔
144
            }
73✔
145

146
            subsets.push_back(filtered_subset);
81✔
147
        }
81✔
148
    }
154✔
149

150
    // No memory accesses to optimize
151
    if (subsets.empty()) {
60✔
152
        return {false, {}};
1✔
153
    }
1✔
154

155
    // Test all permutations (must start from a sorted sequence)
156
    std::vector<std::string> current = permutation;
59✔
157
    std::sort(current.begin(), current.end());
59✔
158

159
    std::vector<std::string> best_permutation;
59✔
160
    std::vector<std::map<size_t, size_t>> best_scores;
59✔
161
    do {
268✔
162
        if (!is_admissible(permutation, current, admissible_swaps)) {
268✔
163
            continue;
151✔
164
        }
151✔
165

166
        // Init scores per dimension
167
        std::vector<std::map<size_t, size_t>> scores;
117✔
168
        for (size_t i = 0; i < current.size(); i++) {
420✔
169
            scores.push_back({});
303✔
170
        }
303✔
171

172
        // Compute skipped dimensions and sum up
173
        for (auto& subset : subsets) {
554✔
174
            std::vector<size_t> strides;
554✔
175
            for (auto& var : current) {
1,451✔
176
                bool found = false;
1,451✔
177
                for (size_t j = 0; j < subset.size(); j++) {
2,634✔
178
                    auto& dim = subset.at(j);
2,063✔
179
                    if (symbolic::uses(dim, var)) {
2,063✔
180
                        strides.push_back(subset.size() - j);
880✔
181
                        found = true;
880✔
182
                        break;
880✔
183
                    }
880✔
184
                }
2,063✔
185
                if (!found) {
1,451✔
186
                    strides.push_back(0);
571✔
187
                }
571✔
188
            }
1,451✔
189

190
            for (size_t i = 0; i < current.size(); i++) {
2,005✔
191
                auto& score_dim = scores[i];
1,451✔
192
                if (score_dim.find(strides[i]) == score_dim.end()) {
1,451✔
193
                    score_dim[strides[i]] = 1;
706✔
194
                } else {
745✔
195
                    score_dim[strides[i]]++;
745✔
196
                }
745✔
197
            }
1,451✔
198
        }
554✔
199

200
        // Compare scores
201
        if (best_permutation.empty()) {
117✔
202
            best_permutation = current;
59✔
203
            best_scores = scores;
59✔
204
        } else {
59✔
205
            bool better = false;
58✔
206
            bool equal = true;
58✔
207
            for (int i = scores.size() - 1; i >= 0; i--) {
72✔
208
                auto& best_score_dim = best_scores[i];
72✔
209
                auto& score_dim = scores[i];
72✔
210

211
                // Decide by maximal stride
212
                auto best_max_stride = best_score_dim.rbegin()->first;
72✔
213
                auto max_stride = score_dim.rbegin()->first;
72✔
214
                if (max_stride < best_max_stride) {
72✔
215
                    better = true;
22✔
216
                    equal = false;
22✔
217
                    break;
22✔
218
                } else if (max_stride == best_max_stride) {
50✔
219
                    // Decide by number of occurences
220
                    auto best_max_stride_count = best_score_dim.rbegin()->second;
16✔
221
                    auto max_stride_count = score_dim.rbegin()->second;
16✔
222
                    if (max_stride_count < best_max_stride_count) {
16✔
UNCOV
223
                        better = true;
×
UNCOV
224
                        equal = false;
×
UNCOV
225
                        break;
×
226
                    } else if (max_stride_count == best_max_stride_count) {
16✔
227
                        continue;
14✔
228
                    } else {
14✔
229
                        equal = false;
2✔
230
                        break;
2✔
231
                    }
2✔
232
                } else {
34✔
233
                    equal = false;
34✔
234
                    break;
34✔
235
                }
34✔
236
            }
72✔
237
            if (better) {
58✔
238
                best_permutation = current;
22✔
239
                best_scores = scores;
22✔
240
            } else if (equal) {
36✔
241
                // If equal and original permutation
UNCOV
242
                if (std::equal(current.begin(), current.end(), permutation.begin())) {
×
UNCOV
243
                    best_permutation = current;
×
UNCOV
244
                    best_scores = scores;
×
UNCOV
245
                }
×
246
            }
×
247
        }
58✔
248
    } while (std::next_permutation(current.begin(), current.end()));
268✔
249

250
    // Check if permutation is better than original
UNCOV
251
    return {best_permutation != permutation, best_permutation};
×
252
};
60✔
253

254
void StrideMinimization::apply(
255
    builder::StructuredSDFGBuilder& builder,
256
    analysis::AnalysisManager& analysis_manager,
257
    std::vector<structured_control_flow::ControlFlowNode*>& nested_loops,
258
    std::vector<std::string> target_permutation
259
) {
6✔
260
    size_t while_loops = 0;
6✔
261
    std::vector<std::string> permutation;
6✔
262
    for (auto& loop : nested_loops) {
17✔
263
        if (!dynamic_cast<structured_control_flow::StructuredLoop*>(loop)) {
17✔
UNCOV
264
            permutation.push_back("WHILE_" + std::to_string(while_loops++));
×
265
        } else {
17✔
266
            auto for_loop = dynamic_cast<structured_control_flow::StructuredLoop*>(loop);
17✔
267
            permutation.push_back(for_loop->indvar()->get_name());
17✔
268
        }
17✔
269
    }
17✔
270
    std::vector<size_t> permutation_indices;
6✔
271
    for (auto& p : permutation) {
17✔
272
        auto it = std::find(target_permutation.begin(), target_permutation.end(), p);
17✔
273
        permutation_indices.push_back(std::distance(target_permutation.begin(), it));
17✔
274
    }
17✔
275

276
    // Bubble sort permutation indices
277
    for (size_t i = 0; i < permutation_indices.size(); i++) {
23✔
278
        for (size_t j = 0; j < permutation_indices.size() - i - 1; j++) {
34✔
279
            if (permutation_indices[j] > permutation_indices[j + 1]) {
17✔
280
                auto first_loop = static_cast<structured_control_flow::StructuredLoop*>(nested_loops.at(j));
6✔
281
                auto second_loop = static_cast<structured_control_flow::StructuredLoop*>(nested_loops.at(j + 1));
6✔
282
                transformations::LoopInterchange loop_interchange(*first_loop, *second_loop);
6✔
283
                if (!loop_interchange.can_be_applied(builder, analysis_manager)) {
6✔
UNCOV
284
                    throw std::runtime_error("Loop interchange cannot be applied");
×
UNCOV
285
                }
×
286
                loop_interchange.apply(builder, analysis_manager);
6✔
287
                std::swap(permutation_indices[j], permutation_indices[j + 1]);
6✔
288
            }
6✔
289
        }
17✔
290
    }
17✔
291
};
6✔
292

UNCOV
293
std::string StrideMinimization::name() { return "StrideMinimization"; };
×
294

295
bool StrideMinimization::run_pass(builder::StructuredSDFGBuilder& builder, analysis::AnalysisManager& analysis_manager) {
30✔
296
    bool applied = false;
30✔
297

298
    auto& loop_tree_analysis = analysis_manager.get<analysis::LoopAnalysis>();
30✔
299

300
    // Collect outermost loops
301
    std::vector<structured_control_flow::ControlFlowNode*> outer_loops = loop_tree_analysis.outermost_loops();
30✔
302

303
    // Apply stride minimization
304
    for (auto& outer_loop : outer_loops) {
66✔
305
        auto& loop_analysis = analysis_manager.get<analysis::LoopAnalysis>();
66✔
306
        auto paths = loop_analysis.loop_tree_paths(outer_loop);
66✔
307
        for (auto& path : paths) {
77✔
308
            auto [applicable, permutation] = this->can_be_applied(builder, analysis_manager, path);
77✔
309
            if (applicable) {
77✔
310
                this->apply(builder, analysis_manager, path, permutation);
6✔
311
                applied = true;
6✔
312
                break;
6✔
313
            }
6✔
314
        }
77✔
315
    }
66✔
316

317
    return applied;
30✔
318
};
30✔
319

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

© 2026 Coveralls, Inc