Skip to content

Commit fbbc73f

Browse files
andyz422copybara-github
authored andcommitted
Create a dpdk_pktgen_benchmark.
PiperOrigin-RevId: 742517271
1 parent 3059d1f commit fbbc73f

17 files changed

+670
-17
lines changed

CHANGES.next.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -216,6 +216,7 @@
216216
- Add support for customizing Hadoop jobs scheduling
217217
- Add support for GKE Autopilot & EKS Auto, which don't require nodepools.
218218
- Add tutorial walking through an example GKE benchmark.
219+
- Add dpdk_pktgen_benchmark, a more feature-rich DPDK benchmark than dpdk_testpmd_benchmark.
219220

220221
### Enhancements:
221222

perfkitbenchmarker/benchmark_sets.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -146,9 +146,7 @@
146146
'cassandra_ycsb',
147147
'cluster_boot',
148148
'copy_throughput',
149-
'dpdk',
150149
'fio',
151-
'fio_netperf',
152150
'gpu_pcie_bandwidth',
153151
'horovod',
154152
'hpcc',
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
set all txburst 1
2+
set all rxburst 1
3+
set all rate <RATE>
4+
enable all range
5+
range all src ip <SRC_IP> <SRC_IP> <SRC_IP> 0.0.0.1
6+
range all dst ip <DST_IP> <DST_IP> <DST_IP> 0.0.0.1
7+
clr
8+
start all
9+
sleep <DURATION>
10+
stop all
11+
sleep 5
12+
exit
Lines changed: 316 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,316 @@
1+
# Copyright 2025 PerfKitBenchmarker Authors. All rights reserved.
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
"""Runs DPDK Pktgen benchmarks for high-performance networking.
15+
16+
DPDK Pktgen Benchmark is a more feature-rich DPDK benchmark than dpdk_testpmd.
17+
DPDK bypasses the kernel networking stack, allowing for much higher PPS.
18+
19+
Benchmark Documentation:
20+
https://pktgen-dpdk.readthedocs.io/en/latest/getting_started.html
21+
https://toonk.io/building-a-high-performance-linux-based-traffic-generator-with-dpdk/index.html
22+
"""
23+
24+
import re
25+
from typing import Any, List, Mapping
26+
27+
from absl import flags
28+
from perfkitbenchmarker import background_tasks
29+
from perfkitbenchmarker import benchmark_spec as bm_spec
30+
from perfkitbenchmarker import configs
31+
from perfkitbenchmarker import linux_virtual_machine
32+
from perfkitbenchmarker import sample
33+
from perfkitbenchmarker.linux_packages import dpdk_pktgen
34+
35+
36+
BENCHMARK_NAME = 'dpdk_pktgen'
37+
BENCHMARK_CONFIG = """
38+
dpdk_pktgen:
39+
description: Runs dpdk testpmd benchmarks
40+
vm_groups:
41+
vm_1:
42+
vm_spec: *default_single_core
43+
vm_2:
44+
vm_spec: *default_single_core
45+
flags:
46+
placement_group_style: closest_supported
47+
gce_subnet_name: default,dpdk0
48+
gce_nic_types: GVNIC,GVNIC
49+
gce_nic_queue_counts: default,default
50+
gce_network_type: custom
51+
ip_addresses: INTERNAL
52+
"""
53+
54+
FLAGS = flags.FLAGS
55+
56+
_DPDK_PKTGEN_DURATION = flags.DEFINE_integer(
57+
'dpdk_pktgen_duration', 60, 'Run duration in seconds.'
58+
)
59+
_DPDK_PKTGEN_PACKET_LOSS_THRESHOLDS = flags.DEFINE_multi_float(
60+
'dpdk_pktgen_packet_loss_threshold_rates',
61+
[0, 0.00001, 0.0001],
62+
'Packet loss thresholds to record samples for.',
63+
lower_bound=0,
64+
)
65+
66+
# DPDK Pktgen maximum logical cores
67+
_MAX_LCORES = 128
68+
# Starting packet transmission rate as a percentage.
69+
_START_RATE = 0.001
70+
# Percent difference in PPS between consecutive iterations to terminate binary
71+
# search.
72+
_PPS_BINARY_SEARCH_THRESHOLD = 0.01
73+
_STDOUT_LOG_FILE = 'pktgen_stdout.log'
74+
75+
76+
def GetConfig(user_config: Mapping[Any, Any]) -> Mapping[Any, Any]:
77+
"""Merge BENCHMARK_CONFIG with user_config to create benchmark_spec.
78+
79+
Args:
80+
user_config: user-defined configs (through FLAGS.benchmark_config_file or
81+
FLAGS.config_override).
82+
83+
Returns:
84+
The resulting configs that come from merging user-defined configs with
85+
BENCHMARK_CONFIG.
86+
"""
87+
return configs.LoadConfig(BENCHMARK_CONFIG, user_config, BENCHMARK_NAME)
88+
89+
90+
def Prepare(benchmark_spec: bm_spec.BenchmarkSpec) -> None:
91+
"""Prepares both VM's to run DPDK Pktgen.
92+
93+
Args:
94+
benchmark_spec: The benchmark specification.
95+
"""
96+
sender_vm, receiver_vm = benchmark_spec.vms[:2]
97+
98+
background_tasks.RunThreaded(
99+
lambda vm: PrepareVM(
100+
vm,
101+
sender_vm.internal_ips[1],
102+
receiver_vm.internal_ips[1],
103+
),
104+
[sender_vm, receiver_vm],
105+
)
106+
receiver_vm.RemoteCommand(
107+
'sudo sed -i "s/set all rate <RATE>//g"'
108+
f' {dpdk_pktgen.DPDK_PKTGEN_GIT_REPO_DIR}/pktgen.pkt'
109+
)
110+
receiver_vm.RemoteCommand(
111+
'sudo sed -i "s/start all//g"'
112+
f' {dpdk_pktgen.DPDK_PKTGEN_GIT_REPO_DIR}/pktgen.pkt'
113+
)
114+
# Ensure receiver runs longer than sender. Receiver starts 1 second before
115+
# sender, so make receiver duration 2 seconds longer.
116+
receiver_vm.RemoteCommand(
117+
f'sudo sed -i "s/<DURATION>/{_DPDK_PKTGEN_DURATION.value+2}/g"'
118+
f' {dpdk_pktgen.DPDK_PKTGEN_GIT_REPO_DIR}/pktgen.pkt'
119+
)
120+
sender_vm.RemoteCommand(
121+
f'sudo sed -i "s/<DURATION>/{_DPDK_PKTGEN_DURATION.value}/g"'
122+
f' {dpdk_pktgen.DPDK_PKTGEN_GIT_REPO_DIR}/pktgen.pkt'
123+
)
124+
125+
126+
def PrepareVM(
127+
vm: linux_virtual_machine.BaseLinuxVirtualMachine,
128+
sender_vm_ip: str,
129+
receiver_vm_ip: str,
130+
) -> None:
131+
"""Prepares a VM to run DPDK Pktgen."""
132+
vm.Install('dpdk_pktgen')
133+
vm.PushDataFile(
134+
'dpdk_pktgen/pktgen.pkt',
135+
f'{dpdk_pktgen.DPDK_PKTGEN_GIT_REPO_DIR}/pktgen.pkt',
136+
)
137+
vm.RemoteCommand(
138+
f'sudo sed -i "s/<SRC_IP>/{sender_vm_ip}/g"'
139+
f' {dpdk_pktgen.DPDK_PKTGEN_GIT_REPO_DIR}/pktgen.pkt'
140+
)
141+
vm.RemoteCommand(
142+
f'sudo sed -i "s/<DST_IP>/{receiver_vm_ip}/g"'
143+
f' {dpdk_pktgen.DPDK_PKTGEN_GIT_REPO_DIR}/pktgen.pkt'
144+
)
145+
146+
147+
def Run(benchmark_spec: bm_spec.BenchmarkSpec) -> list[sample.Sample]:
148+
"""Runs DPDK benchmarks.
149+
150+
Args:
151+
benchmark_spec: The benchmark specification.
152+
153+
Returns:
154+
A list of sample.Sample objects with the performance results.
155+
156+
Raises:
157+
RunError: A run-stage error raised by an individual benchmark.
158+
"""
159+
sender_vm, receiver_vm = benchmark_spec.vms[:2]
160+
samples = []
161+
162+
num_lcores = min(sender_vm.NumCpusForBenchmark(), _MAX_LCORES)
163+
num_memory_channels_stdout, _ = sender_vm.RemoteCommand(
164+
"sudo dmidecode --type memory | grep -c 'Size:'"
165+
)
166+
num_memory_channels = int(num_memory_channels_stdout)
167+
metadata = {
168+
'dpdk_pkgen_burst': 1,
169+
'dpdk_pktgen_lcores': num_lcores,
170+
'dpdk_pktgen_num_memory_channels': num_memory_channels,
171+
'dpdk_pktgen_duration': _DPDK_PKTGEN_DURATION.value,
172+
}
173+
cmd = (
174+
f'cd {dpdk_pktgen.DPDK_PKTGEN_GIT_REPO_DIR} && sudo'
175+
f' ./usr/local/bin/pktgen -l 0-{num_lcores-1} -n {num_memory_channels} --'
176+
f' -m "[1-7].0" -f pktgen.pkt > {_STDOUT_LOG_FILE}'
177+
)
178+
179+
prev_rate = '<RATE>'
180+
for packet_loss_threshold in _DPDK_PKTGEN_PACKET_LOSS_THRESHOLDS.value:
181+
metadata = metadata.copy()
182+
metadata['dpdk_pktgen_packet_loss_threshold'] = packet_loss_threshold
183+
valid_total_sender_tx_pkts = None
184+
valid_total_sender_rx_pkts = None
185+
valid_total_receiver_rx_pkts = None
186+
valid_packet_loss_rate = 1
187+
# Binary search for max PPS under packet loss rate thresholds.
188+
prev_pps, curr_pps = -float('inf'), 0
189+
lb, ub = 0, _START_RATE * 2
190+
191+
while (
192+
abs(curr_pps - prev_pps) / (curr_pps + 1)
193+
) > _PPS_BINARY_SEARCH_THRESHOLD:
194+
curr_rate = (lb + ub) / 2
195+
sender_vm.RemoteCommand(
196+
f'sudo sed -i "s/{prev_rate}/{curr_rate}/g"'
197+
f' {dpdk_pktgen.DPDK_PKTGEN_GIT_REPO_DIR}/pktgen.pkt'
198+
)
199+
200+
# Running Pktgen requires a terminal.
201+
background_tasks.RunThreaded(
202+
lambda vm: vm.RemoteCommand(
203+
cmd, login_shell=True, disable_tty_lock=True
204+
),
205+
[receiver_vm, sender_vm],
206+
post_task_delay=1, # Ensure receiver starts before sender.
207+
)
208+
total_sender_tx_pkts, total_sender_rx_pkts, total_receiver_rx_pkts = (
209+
_ParseStdout(sender_vm, receiver_vm)
210+
)
211+
212+
packet_loss_rate = (
213+
int(total_sender_tx_pkts)
214+
+ int(total_sender_rx_pkts)
215+
- int(total_receiver_rx_pkts)
216+
) / int(total_sender_tx_pkts)
217+
if packet_loss_rate > packet_loss_threshold:
218+
ub = curr_rate
219+
else:
220+
valid_total_sender_tx_pkts = total_sender_tx_pkts
221+
valid_total_sender_rx_pkts = total_sender_rx_pkts
222+
valid_total_receiver_rx_pkts = total_receiver_rx_pkts
223+
valid_packet_loss_rate = packet_loss_rate
224+
lb = curr_rate
225+
prev_pps, curr_pps = (
226+
curr_pps,
227+
int(total_receiver_rx_pkts) // _DPDK_PKTGEN_DURATION.value,
228+
)
229+
prev_rate = curr_rate
230+
231+
samples.extend([
232+
sample.Sample(
233+
'Total sender tx packets',
234+
int(valid_total_sender_tx_pkts),
235+
'packets',
236+
metadata,
237+
),
238+
sample.Sample(
239+
'Total sender tx pps',
240+
int(valid_total_sender_tx_pkts) // _DPDK_PKTGEN_DURATION.value,
241+
'packets/s',
242+
metadata,
243+
),
244+
sample.Sample(
245+
'Total sender rx packets',
246+
int(valid_total_sender_rx_pkts),
247+
'packets',
248+
metadata,
249+
),
250+
sample.Sample(
251+
'Total sender rx pps',
252+
int(valid_total_sender_rx_pkts) // _DPDK_PKTGEN_DURATION.value,
253+
'packets/s',
254+
metadata,
255+
),
256+
sample.Sample(
257+
'Total receiver rx packets',
258+
int(valid_total_receiver_rx_pkts),
259+
'packets',
260+
metadata,
261+
),
262+
sample.Sample(
263+
'Total receiver rx pps',
264+
int(valid_total_receiver_rx_pkts) // _DPDK_PKTGEN_DURATION.value,
265+
'packets/s',
266+
metadata,
267+
),
268+
sample.Sample(
269+
'packet loss rate',
270+
valid_packet_loss_rate,
271+
'rate (1=100%)',
272+
metadata,
273+
),
274+
])
275+
276+
return samples
277+
278+
279+
def _ParseStdout(
280+
sender_vm: linux_virtual_machine.BaseLinuxVirtualMachine,
281+
receiver_vm: linux_virtual_machine.BaseLinuxVirtualMachine,
282+
) -> List[str]:
283+
"""Parse stdout log file to obtain packets sent and received.
284+
285+
Args:
286+
sender_vm: The sender VM.
287+
receiver_vm: The receiver VM.
288+
289+
Returns:
290+
A list of strings representing the total sender tx packets, total sender rx
291+
packets, and total receiver rx packets.
292+
"""
293+
# Filter out ANSI escape codes from stdout.
294+
stdout_parser = (
295+
f'cat {dpdk_pktgen.DPDK_PKTGEN_GIT_REPO_DIR}/{_STDOUT_LOG_FILE} |'
296+
r' sed "s/\x1B\[[0-9;]*[a-zA-Z]//g"'
297+
)
298+
receiver_stdout, _ = receiver_vm.RemoteCommand(stdout_parser)
299+
sender_stdout, _ = sender_vm.RemoteCommand(stdout_parser)
300+
total_sender_rx_pkts, total_sender_tx_pkts = re.findall(
301+
r'Powered by DPDK.*', sender_stdout
302+
)[-1].split()[30:32]
303+
total_receiver_rx_pkts = re.findall(r'Powered by DPDK.*', receiver_stdout)[
304+
-1
305+
].split()[30]
306+
307+
return [total_sender_tx_pkts, total_sender_rx_pkts, total_receiver_rx_pkts]
308+
309+
310+
def Cleanup(benchmark_spec: bm_spec.BenchmarkSpec) -> None:
311+
"""Cleanup benchmarks on the target vm.
312+
313+
Args:
314+
benchmark_spec: The benchmark specification.
315+
"""
316+
del benchmark_spec

perfkitbenchmarker/linux_benchmarks/dpdk_benchmark.py renamed to perfkitbenchmarker/linux_benchmarks/dpdk_testpmd_benchmark.py

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,9 @@
1111
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1212
# See the License for the specific language governing permissions and
1313
# limitations under the License.
14-
"""Runs DPDK Benchmarks for high-performance networking.
14+
"""Runs DPDK Testpmd benchmarks for high-performance networking.
1515
16-
DPDK Benchmark currently contains dpdk-testpmd (Poll-Mode Driver), which
16+
DPDK Testpmd Benchmark currently contains dpdk-testpmd (Poll-Mode Driver), which
1717
measures packets-per-second (PPS) using DPDK. DPDK bypasses the kernel
1818
networking stack, allowing for much higher PPS.
1919
@@ -34,10 +34,10 @@
3434
from perfkitbenchmarker.linux_packages import dpdk
3535

3636

37-
BENCHMARK_NAME = 'dpdk'
37+
BENCHMARK_NAME = 'dpdk_testpmd'
3838
BENCHMARK_CONFIG = """
39-
dpdk:
40-
description: Runs dpdk benchmarks
39+
dpdk_testpmd:
40+
description: Runs dpdk testpmd benchmarks
4141
vm_groups:
4242
vm_1:
4343
vm_spec: *default_single_core
@@ -54,9 +54,6 @@
5454

5555
FLAGS = flags.FLAGS
5656

57-
_DPDK_BENCHMARKS = flags.DEFINE_list(
58-
'dpdk_benchmarks', ['dpdk-testpmd'], 'The dpdk benchmark(s) to run.'
59-
)
6057
_DPDK_TEST_LENGTH = flags.DEFINE_integer(
6158
'dpdk_test_length',
6259
60,

0 commit comments

Comments
 (0)