P4C
The P4 Compiler
Loading...
Searching...
No Matches
characterize_parser.h
1
19#ifndef BACKENDS_TOFINO_BF_P4C_PARDE_CHARACTERIZE_PARSER_H_
20#define BACKENDS_TOFINO_BF_P4C_PARDE_CHARACTERIZE_PARSER_H_
21
22#include "backends/tofino/bf-p4c/bf-p4c-options.h"
23#include "backends/tofino/bf-p4c/common/table_printer.h"
24#include "backends/tofino/bf-p4c/logging/filelog.h"
25#include "backends/tofino/bf-p4c/parde/parser_info.h"
26
32 // Configurable parameters
33
34 unsigned line_rate = 100; // In gigabits
35
36 double clock_rate = 1.22; // In GHz
37
38 // Inter-packet-gap, in bytes
39 std::map<unsigned, unsigned> ipg = {{1, 8}, {25, 5}, {40, 1}, {50, 1}, {100, 1}};
40
41 // preamble + min frame + CRC + gap
42 unsigned target_min_packet_size = 8 + 60 + 4 + ipg[line_rate];
43
44 // Average consumption rates, in bytes per cycle
45 double target_min_avg_rate = line_rate * 1e9 / (8.0 * clock_rate * 1e9);
46
48
50 parser_to_states;
51
52 ordered_map<const IR::BFN::LoweredParserState *,
54 state_to_matches;
55
57 match_to_state;
58
59 struct ExtractorUsage : public ordered_map<PHV::Type, unsigned> {
60 unsigned get(PHV::Type type) const {
61 unsigned rv = 0;
62
63 if (count(type)) rv = (*this).at(type);
64
65 return rv;
66 }
67
68 unsigned get(PHV::Size size) const {
69 unsigned rv = 0;
70
71 for (auto use : *this) {
72 if (use.first.size() == size) rv += use.second;
73 }
74
75 return rv;
76 }
77
78 void add(PHV::Container c) { (*this)[c.type()]++; }
79 };
80
82 match_to_extractor_usage;
83
85
87
88 public:
89 Visitor::profile_t init_apply(const IR::Node *root) override {
90 auto rv = Inspector::init_apply(root);
91 root->apply(cgl);
92
93 BUG_CHECK(!cgl.graphs().empty(), "Parser IR hasn't been lowered yet?");
94 return rv;
95 }
96
97 bool preorder(const IR::BFN::LoweredParserState *state) override {
98 auto parser = findContext<IR::BFN::LoweredParser>();
99 parser_to_states[parser].insert(state);
100 return true;
101 }
102
103 void get_extractor_usage(const IR::BFN::LoweredParserMatch *match) {
104 auto rv = new ExtractorUsage;
105
106 for (auto stmt : match->extracts) {
107 if (auto ep = stmt->to<IR::BFN::LoweredExtractPhv>()) rv->add(ep->dest->container);
108 }
109
110 match_to_extractor_usage[match] = rv;
111 }
112
113 void get_clot_usage(const IR::BFN::LoweredParserMatch *match) {
114 for (auto stmt : match->extracts) {
115 if (auto ec = stmt->to<IR::BFN::LoweredExtractClot>())
116 match_to_clot_usage[match].insert(ec->dest->tag);
117 }
118 }
119
120 void get_checksum_usage(const IR::BFN::LoweredParserMatch *match) {
121 for (auto csum : match->checksums) match_to_checksum_usage[match].insert(csum->unit_id);
122 }
123
124 bool preorder(const IR::BFN::LoweredParserMatch *match) override {
125 auto state = findContext<IR::BFN::LoweredParserState>();
126 state_to_matches[state].insert(match);
127 match_to_state[match] = state;
128
129 get_extractor_usage(match);
130 get_clot_usage(match);
131 get_checksum_usage(match);
132
133 return true;
134 }
135
136 void print_state_usage(const IR::BFN::LoweredParser *parser) {
137 if (!parser_to_states.count(parser)) return;
138
139 std::string total_extract_label = "Total Extractors";
140 if (Device::currentDevice() != Device::TOFINO) total_extract_label += " (16-bit)";
141
142 TablePrinter tp(
143 std::clog,
144 {"State", "Match", "8-bit", "16-bit", "32-bit", total_extract_label, "Other"},
145 TablePrinter::Align::LEFT);
146
147 auto sorted = cgl.graphs().at(parser)->topological_sort();
148
149 for (auto s : sorted) {
150 if (state_to_matches.count(s)) {
151 std::string state_label;
152
153 for (auto m : state_to_matches.at(s)) {
154 auto &usage = match_to_extractor_usage.at(m);
155 auto state = match_to_state.at(m);
156
157 auto usage_8b = usage->get(PHV::Size::b8);
158 auto usage_16b = usage->get(PHV::Size::b16);
159 auto usage_32b = usage->get(PHV::Size::b32);
160
161 unsigned sausage = 0; // state aggreated usage
162
163 if (Device::currentDevice() == Device::TOFINO)
164 sausage = usage_8b + usage_16b + usage_32b;
165 else if (Device::currentDevice() != Device::TOFINO)
166 sausage = usage_8b + usage_16b + 2 * usage_32b;
167
168 if (state_label.empty())
169 state_label = std::string(state->name);
170 else
171 state_label = "-";
172
173 std::stringstream other_usage;
174
175 if (match_to_clot_usage.count(m)) {
176 other_usage << "clot ";
177 for (auto c : match_to_clot_usage.at(m)) other_usage << c << " ";
178 }
179
180 if (match_to_checksum_usage.count(m)) {
181 other_usage << "csum ";
182 for (auto c : match_to_checksum_usage.at(m)) other_usage << c << " ";
183 }
184
185 tp.addRow({state_label, std::string(m->value->toString()),
186 std::to_string(usage_8b), std::to_string(usage_16b),
187 std::to_string(usage_32b), std::to_string(sausage),
188 other_usage.str()});
189 }
190 }
191 }
192
193 tp.print();
194 }
195
196 static double round(double val, int decimal_point) {
197 unsigned round_by = pow(10, decimal_point);
198 return (val * round_by) / round_by;
199 }
200
201 void print_timing_report(const IR::BFN::LoweredParser *parser,
202 const std::vector<const IR::BFN::LoweredParserState *> &path) {
203 unsigned total_user_header_bits = 0;
204
205 for (auto it = path.begin(); it != path.end(); it++) {
206 // need a better way of delineating metadata states vs. user header states
207 if ((*it)->name.startsWith("$")) continue;
208
209 auto next = std::next(it);
210
211 if (next != path.end()) {
212 auto matches = cgl.graphs().at(parser)->transitions(*it, *next);
213 if (!matches.empty()) total_user_header_bits += (*matches.begin())->shift;
214 } else {
215 auto matches = cgl.graphs().at(parser)->to_pipe(*it);
216 if (!matches.empty()) total_user_header_bits += (*matches.begin())->shift;
217 }
218 }
219
220 if (total_user_header_bits < target_min_packet_size * 8)
221 total_user_header_bits = target_min_packet_size * 8;
222
223 double avg_rate = total_user_header_bits / (8.0 * path.size());
224
225 if (avg_rate < target_min_avg_rate) {
226 unsigned min_packet_size = std::max(
227 target_min_packet_size, unsigned(ceil(target_min_avg_rate * (path.size()))));
228
229 unsigned min_payload_size = min_packet_size - total_user_header_bits / 8;
230 double max_data_rate = clock_rate * (total_user_header_bits / path.size());
231 double max_packet_rate = (1e3 * max_data_rate) / total_user_header_bits;
232
233 max_data_rate = round(max_data_rate, 2);
234 max_packet_rate = round(max_packet_rate, 2);
235
236 std::clog << "Average rate: " << avg_rate << " Bps" << std::endl;
237
238 std::clog << "Min packet size at " << line_rate << " Gbps: " << min_packet_size << " B"
239 << " (" << min_payload_size << " B payload)" << std::endl;
240
241 std::clog << "Max data rate for min-sized packets: " << max_data_rate << " Gbps / "
242 << max_packet_rate << " MPps" << std::endl;
243 } else {
244 std::clog << "Timing is met for min-sized packet (" << target_min_packet_size << " B)"
245 << " running at " << line_rate << " Gbps" << std::endl;
246 }
247 }
248
249 void print_parser_summary(const IR::BFN::LoweredParser *parser) {
250 unsigned num_states = 0;
251 unsigned num_matches = 0;
252
253 if (parser_to_states.count(parser)) {
254 num_states = parser_to_states.at(parser).size();
255
256 for (auto s : parser_to_states.at(parser)) {
257 if (state_to_matches.count(s)) num_matches += state_to_matches.at(s).size();
258 }
259 }
260
261 auto gress = parser->gress;
262
263 std::clog << "Number of states on " << gress << ": " << num_states << std::endl;
264
265 std::clog << std::endl;
266
267 std::clog << "Number of matches on " << gress << ": " << num_matches << std::endl;
268
269 std::clog << std::endl;
270
271 auto longest_path = cgl.graphs().at(parser)->longest_path_states(parser->start);
272
273 std::clog << "Longest path (" << longest_path.size() << " states) on " << gress << ":"
274 << std::endl;
275
276 for (auto s : longest_path) std::clog << " " << s->name << std::endl;
277
278 std::clog << std::endl;
279
280 auto shortest_path = cgl.graphs().at(parser)->shortest_path_states(parser->start);
281
282 std::clog << "Shortest path (" << shortest_path.size() << " states) on " << gress << ":"
283 << std::endl;
284
285 for (auto s : shortest_path) std::clog << " " << s->name << std::endl;
286
287 std::clog << std::endl;
288
289 print_timing_report(parser, longest_path);
290
291 std::clog << std::endl;
292
293 std::clog << "Extractor usage:" << std::endl;
294
295 print_state_usage(parser);
296 }
297
298 void end_apply(const IR::Node *root) override {
299 const IR::BFN::Pipe *pipe = root->to<IR::BFN::Pipe>();
300 if (BackendOptions().verbose > 0) {
301 Logging::FileLog parserLog(pipe->canon_id(), cstring("parser.characterize.log"));
302
303 std::clog << "Parser Characterization Report:" << std::endl;
304
305 for (auto ps : parser_to_states) print_parser_summary(ps.first);
306 }
307 }
308};
309
310#endif /* BACKENDS_TOFINO_BF_P4C_PARDE_CHARACTERIZE_PARSER_H_ */
A FileLog is used to redirect the logging output of a visitor pass to a file.
Definition filelog.h:48
Definition node.h:94
Definition visitor.h:400
Definition visitor.h:78
Definition cstring.h:85
Definition ordered_map.h:32
Definition ordered_set.h:32
Definition phv.h:176
Definition phv.h:114
Definition table_printer.h:40
Prints various info about parser to the log file.
Definition characterize_parser.h:31
Size
all possible PHV container sizes in BFN devices
Definition phv.h:110