Skip to content

Commit a25f55d

Browse files
nikhildl12facebook-github-bot
authored andcommitted
Fix perf testing mode in katran_tester
Summary: katran tester has perf tesing mode to meause time took for running xdp program for input packet. This flow was broken and needed some fixes: 1. Updates to the script, selected gue mode 2. Fix calls to bpf_prog_test_run with repeat argument. Repeat isn't useful if we xdp prog modifies packet inline, the next runs sees updated packet and skips it 3. Skip LRU for test vips. This way we run same bpf instructions on every repeat run 4. Added printing results in a neat table, sorted by duration. This way we can check the most expensive input packet, in terms of processing time in XDP Reviewed By: tagrawal03 Differential Revision: D81096115 fbshipit-source-id: a3ee4977a59c90f44496831baae3c3269c500d91
1 parent 69c5a4b commit a25f55d

5 files changed

Lines changed: 179 additions & 26 deletions

File tree

katran/lib/testing/framework/BpfTester.cpp

Lines changed: 51 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,7 @@ void BpfTester::testPcktsFromPcap() {
100100
VLOG(2) << "we have read all the packets from pcap file";
101101
break;
102102
}
103+
103104
auto res = adapter_.testXdpProg(
104105
config_.bpfProgFd,
105106
kTestRepeatCount,
@@ -413,50 +414,79 @@ void BpfTester::resetTestFixtures(const std::vector<PacketAttributes>& data) {
413414
config_.testData = data;
414415
}
415416

416-
void BpfTester::testPerfFromFixture(uint32_t repeat, const int position) {
417+
std::vector<TestResult> BpfTester::testPerfFromFixture(
418+
uint32_t repeat,
419+
const int position) {
417420
// for inputData format is <pckt_base64, test description>
418421
int first_index{0}, last_index{0};
419-
uint32_t duration{0};
420-
uint64_t pckt_num{1};
421-
std::string ret_val_str;
422-
std::string test_result;
423422
if (position < 0 || position >= config_.testData.size()) {
424423
first_index = 0;
425424
last_index = config_.testData.size();
426425
} else {
427426
first_index = position;
428427
last_index = first_index + 1;
429428
}
429+
430+
std::vector<TestResult> results;
431+
432+
// Run all tests and collect results
430433
for (int i = first_index; i < last_index; i++) {
431-
auto buf = folly::IOBuf::create(kMaxXdpPcktSize);
432-
auto input_pckt =
433-
parser_.getPacketFromBase64(config_.testData[i].inputPacket);
434+
auto single_results = runXdpProgPerf(
435+
config_.testData[i].inputPacket,
436+
config_.testData[i].description,
437+
repeat);
438+
results.insert(results.end(), single_results.begin(), single_results.end());
439+
}
440+
return results;
441+
}
442+
443+
std::vector<TestResult> BpfTester::runXdpProgPerf(
444+
const std::string& input_packet,
445+
const std::string& description,
446+
uint32_t repeat) {
447+
std::vector<TestResult> results;
448+
results.reserve(repeat); // Pre-allocate capacity
449+
450+
auto buf = folly::IOBuf::create(kMaxXdpPcktSize);
451+
uint32_t output_pckt_size{0};
452+
453+
auto input_pckt = parser_.getPacketFromBase64(input_packet);
454+
455+
// Passing repeat directly to testXdpProg() will cause the input_pckt to be
456+
// modified with the first run and the subsequent runs will see the encaped
457+
// packet. For more acurate perf runs, we call testXdpProg() repeatedly with
458+
// repeat = 1
459+
460+
uint32_t total_duration = 0;
461+
for (uint32_t run = 0; run < repeat; ++run) {
462+
uint32_t duration{0};
434463
auto res = adapter_.testXdpProg(
435464
config_.bpfProgFd,
436-
repeat,
465+
1, // repeat = 1
437466
input_pckt->writableData(),
438467
input_pckt->length(),
439468
buf->writableData(),
440-
nullptr, // output pckt size
469+
&output_pckt_size,
441470
nullptr, // retval
442471
&duration);
472+
443473
if (res < 0) {
444-
LOG(INFO) << "failed to run bpf test on pckt #" << pckt_num;
445-
++pckt_num;
446-
continue;
474+
LOG(ERROR) << "failed to run bpf test on run #" << run;
475+
break;
447476
}
448-
VLOG(2) << "pckt #" << pckt_num;
477+
total_duration += duration;
478+
449479
if (duration == 0) {
450480
duration = 1;
451481
}
452-
auto pps = kNanosecInSec / duration;
453-
LOG(INFO) << fmt::format(
454-
"Test: {:60} duration: {:10} ns/pckt or {} pps",
455-
config_.testData[i].description,
456-
duration,
457-
pps);
458-
++pckt_num;
459482
}
483+
if (repeat > 0) {
484+
auto avgDuration = total_duration / repeat;
485+
auto pps = kNanosecInSec / avgDuration;
486+
auto pps_millions = static_cast<double>(pps) / 1000000.0;
487+
results.push_back({description, avgDuration, pps_millions});
488+
}
489+
return results;
460490
}
461491

462492
uint64_t BpfTester::getGlobalLruRoutedPackets() {

katran/lib/testing/framework/BpfTester.h

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,13 @@
2828

2929
namespace katran {
3030

31+
// Structure to hold test results for perf testing runs
32+
struct TestResult {
33+
std::string description;
34+
uint32_t duration;
35+
double pps_millions;
36+
};
37+
3138
/**
3239
* structure with config params for BpfTester.
3340
*/
@@ -151,8 +158,11 @@ class BpfTester {
151158
* @param int position of the packet if fixtures vector.
152159
* helper function to run perf test on specified packet from test fixtures
153160
* if position is negative - run perf tests on every packet in fixtures
161+
* @return std::vector<TestResult> results from the performance tests
154162
*/
155-
void testPerfFromFixture(uint32_t repeat, const int position = -1);
163+
std::vector<TestResult> testPerfFromFixture(
164+
uint32_t repeat,
165+
const int position = -1);
156166

157167
/**
158168
* @param IOBuf with packet data to write.
@@ -184,6 +194,18 @@ class BpfTester {
184194
const std::string& actualPacket,
185195
const std::string& expectedPacket);
186196

197+
/**
198+
* @param const std::string& input_packet base64 encoded input packet
199+
* @param const std::string& description test description for the results
200+
* @param uint32_t repeat how many times to repeat the test
201+
* helper function to run performance tests in a loop with repeat=1 and return
202+
* results
203+
*/
204+
std::vector<struct TestResult> runXdpProgPerf(
205+
const std::string& input_packet,
206+
const std::string& description,
207+
uint32_t repeat);
208+
187209
TesterConfig config_;
188210
PcapParser parser_;
189211
BpfAdapter adapter_;

katran/lib/testing/framework/katran_tester.cpp

Lines changed: 75 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -248,6 +248,78 @@ KatranTestParam getTestParam() {
248248
}
249249
}
250250

251+
// Function to print performance test results in a formatted table
252+
// sorted by duration in descending order
253+
void printPerfResults(std::vector<katran::TestResult>& results) {
254+
if (results.empty()) {
255+
std::cout << "No performance test results to display." << std::endl;
256+
return;
257+
}
258+
// Sort results by duration in descending order (largest on top)
259+
std::sort(
260+
results.begin(),
261+
results.end(),
262+
[](const katran::TestResult& a, const katran::TestResult& b) {
263+
return a.duration > b.duration;
264+
});
265+
266+
// Calculate dynamic column widths
267+
constexpr int duration_width = 12;
268+
constexpr int pps_width = 12;
269+
constexpr int min_desc_width = 40;
270+
constexpr int max_desc_width = 80;
271+
272+
// Find the longest description to determine optimal width
273+
int max_desc_len = 0;
274+
for (const auto& result : results) {
275+
max_desc_len =
276+
std::max(max_desc_len, static_cast<int>(result.description.length()));
277+
}
278+
279+
int desc_width =
280+
std::max(min_desc_width, std::min(max_desc_width, max_desc_len));
281+
282+
// Print table header
283+
std::cout << "+" << std::string(desc_width + 2, '-') << "+"
284+
<< std::string(duration_width + 2, '-') << "+"
285+
<< std::string(pps_width + 2, '-') << "+" << std::endl;
286+
std::cout << fmt::format(
287+
"| {:<{}} | {:>{}} | {:>{}} |",
288+
"Test Description",
289+
desc_width,
290+
"Duration",
291+
duration_width,
292+
"PPS",
293+
pps_width)
294+
<< std::endl;
295+
std::cout << "+" << std::string(desc_width + 2, '-') << "+"
296+
<< std::string(duration_width + 2, '-') << "+"
297+
<< std::string(pps_width + 2, '-') << "+" << std::endl;
298+
299+
// Print sorted results
300+
for (const auto& result : results) {
301+
// Truncate description if it's too long
302+
std::string desc = result.description;
303+
if (desc.length() > desc_width) {
304+
desc = desc.substr(0, desc_width - 3) + "...";
305+
}
306+
307+
std::cout << fmt::format(
308+
"| {:<{}} | {:>{}} ns | {:>8.2f} Mpps |",
309+
desc,
310+
desc_width,
311+
result.duration,
312+
duration_width - 3,
313+
result.pps_millions)
314+
<< std::endl;
315+
}
316+
317+
// Print table footer
318+
std::cout << "+" << std::string(desc_width + 2, '-') << "+"
319+
<< std::string(duration_width + 2, '-') << "+"
320+
<< std::string(pps_width + 2, '-') << "+" << std::endl;
321+
}
322+
251323
int main(int argc, char** argv) {
252324
gflags::ParseCommandLineFlags(&argc, &argv, true);
253325
google::InitGoogleLogging(argv[0]);
@@ -321,14 +393,15 @@ int main(int argc, char** argv) {
321393
}
322394
return 0;
323395
}
324-
prepareLbData(*lb);
396+
prepareLbData(*lb, FLAGS_perf_testing);
325397
if (!FLAGS_pcap_input.empty()) {
326398
tester.testPcktsFromPcap();
327399
return 0;
328400
} else if (FLAGS_perf_testing) {
329401
// for perf tests to work katran must be compiled w -DINLINE_DECAP
330402
preparePerfTestingLbData(*lb);
331-
tester.testPerfFromFixture(FLAGS_repeat, FLAGS_position);
403+
auto results = tester.testPerfFromFixture(FLAGS_repeat, FLAGS_position);
404+
printPerfResults(results);
332405
}
333406
return 0;
334407
}

katran/lib/testing/utils/KatranTestProvision.cpp

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -105,32 +105,45 @@ void addQuicMappings(katran::KatranLb& lb) {
105105
lb.modifyQuicRealsMapping(action, qreals);
106106
}
107107

108-
void prepareLbData(katran::KatranLb& lb) {
108+
void prepareLbData(katran::KatranLb& lb, bool skipLru) {
109109
lb.restartKatranMonitor(kMonitorLimit);
110110
katran::VipKey vip;
111111
// adding udp vip for tests
112112
vip.address = "10.200.1.1";
113113
vip.port = kVipPort;
114114
vip.proto = kUdp;
115115
lb.addVip(vip);
116+
if (skipLru) {
117+
LOG(INFO) << "Skipping LRU lookup for vip " << vip.address;
118+
lb.modifyVip(vip, kBypassLruLookup);
119+
}
116120
// adding few reals to test
117121
std::vector<std::string> reals = {"10.0.0.1", "10.0.0.2", "10.0.0.3"};
118122
std::vector<std::string> reals6 = {"fc00::1", "fc00::2", "fc00::3"};
119123
addReals(lb, vip, reals);
120124
// adding tcp vip for tests
121125
vip.proto = kTcp;
122126
lb.addVip(vip);
127+
if (skipLru) {
128+
lb.modifyVip(vip, kBypassLruLookup);
129+
}
123130
// adding few reals to test
124131
addReals(lb, vip, reals);
125132
// vip which ignores dst_port (testing for TURN-like services)
126133
vip.address = "10.200.1.2";
127134
vip.port = 0;
128135
lb.addVip(vip);
136+
if (skipLru) {
137+
lb.modifyVip(vip, kBypassLruLookup);
138+
}
129139
// adding few reals to test
130140
addReals(lb, vip, reals);
131141
// vip which is using only dst port to pick up real
132142
vip.address = "10.200.1.4";
133143
lb.addVip(vip);
144+
if (skipLru) {
145+
lb.modifyVip(vip, kBypassLruLookup);
146+
}
134147
// adding few reals to test
135148
addReals(lb, vip, reals);
136149
lb.modifyVip(vip, kDportHash);
@@ -143,6 +156,9 @@ void prepareLbData(katran::KatranLb& lb) {
143156
// v6inv6 vip. tcp
144157
vip.address = "fc00:1::1";
145158
lb.addVip(vip);
159+
if (skipLru) {
160+
lb.modifyVip(vip, kBypassLruLookup);
161+
}
146162
// adding few reals to test
147163
addReals(lb, vip, reals6);
148164
// adding mappings for quic.
@@ -152,18 +168,27 @@ void prepareLbData(katran::KatranLb& lb) {
152168
vip.port = 443;
153169
vip.address = "10.200.1.5";
154170
lb.addVip(vip);
171+
if (skipLru) {
172+
lb.modifyVip(vip, kBypassLruLookup);
173+
}
155174
lb.modifyVip(vip, kQuicVip);
156175
addReals(lb, vip, reals);
157176
// adding quic v6 vip.
158177
vip.address = "fc00:1::2";
159178
lb.addVip(vip);
179+
if (skipLru) {
180+
lb.modifyVip(vip, kBypassLruLookup);
181+
}
160182
lb.modifyVip(vip, kQuicVip);
161183
addReals(lb, vip, reals6);
162184
// adding udp with flow migration vip for tests
163185
vip.address = "10.200.1.6";
164186
vip.port = kVipPort;
165187
vip.proto = kUdp;
166188
lb.addVip(vip);
189+
if (skipLru) {
190+
lb.modifyVip(vip, kBypassLruLookup);
191+
}
167192
lb.modifyVip(vip, kUdpFlowMigration);
168193
addReals(lb, vip, reals);
169194
// setting a real to be down

katran/lib/testing/utils/KatranTestProvision.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,9 @@ constexpr uint32_t kDefaultWeight = 1;
4444
// Flags from katran/lib/bpf/balancer_consts.h
4545
// real is specified as local (1 << 1)
4646
constexpr uint8_t kLocalReal = 2;
47+
48+
// F_LRU_BYPASS
49+
constexpr uint32_t kBypassLruLookup = 2; // (1 << 1)
4750
// use quic's connection id for the hash calculation (1 << 2)
4851
constexpr uint32_t kQuicVip = 4;
4952
// use only dst port for the hash calculation (1 << 3)
@@ -166,7 +169,7 @@ void deleteReals(
166169

167170
void addQuicMappings(katran::KatranLb& lb);
168171

169-
void prepareLbData(katran::KatranLb& lb);
172+
void prepareLbData(katran::KatranLb& lb, bool skipLru = false);
170173

171174
void prepareOptionalLbData(katran::KatranLb& lb);
172175

0 commit comments

Comments
 (0)