From: Andrew Bailey <abailey@iol.unh.edu>
To: Patrick Robb <probb@iol.unh.edu>
Cc: Luca.Vizzarro@arm.com, dev@dpdk.org, Paul.Szczepanek@arm.com,
dmarx@iol.unh.edu, Nicholas Pratte <npratte@iol.unh.edu>
Subject: Re: [PATCH v6 3/3] dts: add performance test functions to test suite API
Date: Thu, 6 Nov 2025 08:30:46 -0500 [thread overview]
Message-ID: <CABJ3N2WsSuKhij2XtU1vo6E+dsuGyf9p0sQvaDM1hEhRcxU6eQ@mail.gmail.com> (raw)
In-Reply-To: <20251105223628.1659390-4-probb@iol.unh.edu>
[-- Attachment #1: Type: text/plain, Size: 15069 bytes --]
+ params["measured_mpps"] = self._transmit(testpmd,
frame_size)
+ params["performance_delta"] = (
+ float(params["measured_mpps"]) -
float(params["expected_mpps"])
+ ) / float(params["expected_mpps"])
+ params["pass"] = float(params["performance_delta"]) >=
-self.delta_tolerance
This code seems like it can produce false positives. If we are checking if
a measured mpps is within dela_tolerance of the expected, (pass =
measured_mpps >= expected_mpps - delta_tolerance) may be more effective. As
an example of a false positive, use measured_mpps = 1.0, expected_mpps =
2.0, and delta_tolerance = 0.51. This should be a fail since 1.0 is not
within 0.51 of 2.0 but equates to (-.50) >= (-.51) == true.
On Wed, Nov 5, 2025 at 5:37 PM Patrick Robb <probb@iol.unh.edu> wrote:
> From: Nicholas Pratte <npratte@iol.unh.edu>
>
> Provide packet transmission function to support performance tests using a
> user-supplied performance traffic generator. The single core performance
> test is included. It allows the user to define a matrix of frame size,
> descriptor count, and expected mpps, and fails if any combination does
> not forward a mpps count within 5% of the given baseline.
>
> Bugzilla ID: 1697
> Signed-off-by: Nicholas Pratte <npratte@iol.unh.edu>
> Signed-off-by: Patrick Robb <probb@iol.unh.edu>
> Reviewed-by: Dean Marx <dmarx@iol.unh.edu>
> Reviewed-by: Andrew Bailey <abailey@iol.unh.edu>
> ---
> ...sts.TestSuite_single_core_forward_perf.rst | 8 +
> dts/api/packet.py | 35 +++-
> dts/api/test.py | 32 ++++
> dts/configurations/tests_config.example.yaml | 12 ++
> .../TestSuite_single_core_forward_perf.py | 149 ++++++++++++++++++
> 5 files changed, 235 insertions(+), 1 deletion(-)
> create mode 100644
> doc/api/dts/tests.TestSuite_single_core_forward_perf.rst
> create mode 100644 dts/tests/TestSuite_single_core_forward_perf.py
>
> diff --git a/doc/api/dts/tests.TestSuite_single_core_forward_perf.rst
> b/doc/api/dts/tests.TestSuite_single_core_forward_perf.rst
> new file mode 100644
> index 0000000000..3651b0b041
> --- /dev/null
> +++ b/doc/api/dts/tests.TestSuite_single_core_forward_perf.rst
> @@ -0,0 +1,8 @@
> +.. SPDX-License-Identifier: BSD-3-Clause
> +
> +single_core_forward_perf Test Suite
> +===================================
> +
> +.. automodule:: tests.TestSuite_single_core_forward_perf
> + :members:
> + :show-inheritance:
> diff --git a/dts/api/packet.py b/dts/api/packet.py
> index ac7f64dd17..094a1b7a9d 100644
> --- a/dts/api/packet.py
> +++ b/dts/api/packet.py
> @@ -33,6 +33,9 @@
> from
> framework.testbed_model.traffic_generator.capturing_traffic_generator
> import (
> PacketFilteringConfig,
> )
> +from
> framework.testbed_model.traffic_generator.performance_traffic_generator
> import (
> + PerformanceTrafficStats,
> +)
> from framework.utils import get_packet_summaries
>
>
> @@ -108,7 +111,9 @@ def send_packets(
> packets: Packets to send.
> """
> packets = adjust_addresses(packets)
> - get_ctx().func_tg.send_packets(packets,
> get_ctx().topology.tg_port_egress)
> + tg = get_ctx().func_tg
> + if tg:
> + tg.send_packets(packets, get_ctx().topology.tg_port_egress)
>
>
> def get_expected_packets(
> @@ -317,3 +322,31 @@ def _verify_l3_packet(received_packet: IP,
> expected_packet: IP) -> bool:
> if received_packet.src != expected_packet.src or received_packet.dst
> != expected_packet.dst:
> return False
> return True
> +
> +
> +def assess_performance_by_packet(
> + packet: Packet, duration: float, send_mpps: int | None = None
> +) -> PerformanceTrafficStats:
> + """Send a given packet for a given duration and assess basic
> performance statistics.
> +
> + Send `packet` and assess NIC performance for a given duration,
> corresponding to the test
> + suite's given topology.
> +
> + Args:
> + packet: The packet to send.
> + duration: Performance test duration (in seconds).
> + send_mpps: The millions packets per second send rate.
> +
> + Returns:
> + Performance statistics of the generated test.
> + """
> + from
> framework.testbed_model.traffic_generator.performance_traffic_generator
> import (
> + PerformanceTrafficGenerator,
> + )
> +
> + assert isinstance(
> + get_ctx().perf_tg, PerformanceTrafficGenerator
> + ), "Cannot send performance traffic with non-performance traffic
> generator"
> + tg: PerformanceTrafficGenerator = cast(PerformanceTrafficGenerator,
> get_ctx().perf_tg)
> + # TODO: implement @requires for types of traffic generator
> + return tg.calculate_traffic_and_stats(packet, duration, send_mpps)
> diff --git a/dts/api/test.py b/dts/api/test.py
> index f58c82715d..11265ee2c1 100644
> --- a/dts/api/test.py
> +++ b/dts/api/test.py
> @@ -6,9 +6,13 @@
> This module provides utility functions for test cases, including logging,
> verification.
> """
>
> +import json
> +from datetime import datetime
> +
> from framework.context import get_ctx
> from framework.exception import InternalError, SkippedTestException,
> TestCaseVerifyError
> from framework.logger import DTSLogger
> +from framework.testbed_model.artifact import Artifact
>
>
> def get_current_test_case_name() -> str:
> @@ -124,3 +128,31 @@ def get_logger() -> DTSLogger:
> if current_test_suite is None:
> raise InternalError("No current test suite")
> return current_test_suite._logger
> +
> +
> +def write_performance_json(
> + performance_data: dict, filename: str = "performance_metrics.json"
> +) -> None:
> + """Write performance test results to a JSON file in the test suite's
> output directory.
> +
> + This method creates a JSON file containing performance metrics in the
> test suite's
> + output directory. The data can be a dictionary of any structure. No
> specific format
> + is required.
> +
> + Args:
> + performance_data: Dictionary containing performance metrics and
> results.
> + filename: Name of the JSON file to create.
> +
> + Raises:
> + InternalError: If performance data is not provided.
> + """
> + if not performance_data:
> + raise InternalError("No performance data to write")
> +
> + perf_data = {"timestamp": datetime.now().isoformat(),
> **performance_data}
> + perf_json_artifact = Artifact("local", filename)
> +
> + with perf_json_artifact.open("w") as json_file:
> + json.dump(perf_data, json_file, indent=2)
> +
> + get_logger().info(f"Performance results written to:
> {perf_json_artifact.local_path}")
> diff --git a/dts/configurations/tests_config.example.yaml
> b/dts/configurations/tests_config.example.yaml
> index c011ac0588..167bc91a35 100644
> --- a/dts/configurations/tests_config.example.yaml
> +++ b/dts/configurations/tests_config.example.yaml
> @@ -3,3 +3,15 @@
> # Define the custom test suite configurations
> hello_world:
> msg: A custom hello world to you!
> +single_core_forward_perf:
> + test_parameters: # Add frame size / descriptor count combinations as
> needed
> + - frame_size: 64
> + num_descriptors: 512
> + expected_mpps: 1.0 # Set millions of packets per second according
> to your devices expected throughput for this given frame size / descriptor
> count
> + - frame_size: 64
> + num_descriptors: 1024
> + expected_mpps: 1.0
> + - frame_size: 512
> + num_descriptors: 1024
> + expected_mpps: 1.0
> + delta_tolerance: 0.05
> \ No newline at end of file
> diff --git a/dts/tests/TestSuite_single_core_forward_perf.py
> b/dts/tests/TestSuite_single_core_forward_perf.py
> new file mode 100644
> index 0000000000..8a92ba39b5
> --- /dev/null
> +++ b/dts/tests/TestSuite_single_core_forward_perf.py
> @@ -0,0 +1,149 @@
> +# SPDX-License-Identifier: BSD-3-Clause
> +# Copyright(c) 2025 University of New Hampshire
> +
> +"""Single core forwarding performance test suite.
> +
> +This suite measures the amount of packets which can be forwarded by DPDK
> using a single core.
> +The testsuites takes in as parameters a set of parameters, each
> consisting of a frame size,
> +Tx/Rx descriptor count, and the expected MPPS to be forwarded by the DPDK
> application. The
> +test leverages a performance traffic generator to send traffic at two
> paired TestPMD interfaces
> +on the SUT system, which forward to one another and then back to the
> traffic generator's ports.
> +The aggregate packets forwarded by the two TestPMD ports are compared
> against the expected MPPS
> +baseline which is given in the test config, in order to determine the
> test result.
> +"""
> +
> +from scapy.layers.inet import IP
> +from scapy.layers.l2 import Ether
> +from scapy.packet import Raw
> +
> +from api.capabilities import (
> + LinkTopology,
> + requires_link_topology,
> +)
> +from api.packet import assess_performance_by_packet
> +from api.test import verify, write_performance_json
> +from api.testpmd import TestPmd
> +from api.testpmd.config import RXRingParams, TXRingParams
> +from framework.params.types import TestPmdParamsDict
> +from framework.test_suite import BaseConfig, TestSuite, perf_test
> +
> +
> +class Config(BaseConfig):
> + """Performance test metrics."""
> +
> + test_parameters: list[dict[str, int | float]] = [
> + {"frame_size": 64, "num_descriptors": 1024, "expected_mpps":
> 1.00},
> + {"frame_size": 128, "num_descriptors": 1024, "expected_mpps":
> 1.00},
> + {"frame_size": 256, "num_descriptors": 1024, "expected_mpps":
> 1.00},
> + {"frame_size": 512, "num_descriptors": 1024, "expected_mpps":
> 1.00},
> + {"frame_size": 1024, "num_descriptors": 1024, "expected_mpps":
> 1.00},
> + {"frame_size": 1518, "num_descriptors": 1024, "expected_mpps":
> 1.00},
> + ]
> + delta_tolerance: float = 0.05
> +
> +
> +@requires_link_topology(LinkTopology.TWO_LINKS)
> +class TestSingleCoreForwardPerf(TestSuite):
> + """Single core forwarding performance test suite."""
> +
> + config: Config
> +
> + def set_up_suite(self):
> + """Set up the test suite."""
> + self.test_parameters = self.config.test_parameters
> + self.delta_tolerance = self.config.delta_tolerance
> +
> + def _transmit(self, testpmd: TestPmd, frame_size: int) -> float:
> + """Create a testpmd session with every rule in the given list,
> verify jump behavior.
> +
> + Args:
> + testpmd: The testpmd shell to use for forwarding packets.
> + frame_size: The size of the frame to transmit.
> +
> + Returns:
> + The MPPS (millions of packets per second) forwarded by the
> SUT.
> + """
> + # Build packet with dummy values, and account for the 14B and 20B
> Ether and IP headers
> + packet = (
> + Ether(src="52:00:00:00:00:00")
> + / IP(src="1.2.3.4", dst="192.18.1.0")
> + / Raw(load="x" * (frame_size - 14 - 20))
> + )
> +
> + testpmd.start()
> +
> + # Transmit for 30 seconds.
> + stats = assess_performance_by_packet(packet=packet, duration=30)
> +
> + rx_mpps = stats.rx_pps / 1_000_000
> +
> + return rx_mpps
> +
> + def _produce_stats_table(self, test_parameters: list[dict[str, int |
> float]]) -> None:
> + """Display performance results in table format and write to
> structured JSON file.
> +
> + Args:
> + test_parameters: The expected and real stats per set of test
> parameters.
> + """
> + header = f"{'Frame Size':>12} | {'TXD/RXD':>12} | {'Real
> MPPS':>12} | {'Expected MPPS':>14}"
> + print("-" * len(header))
> + print(header)
> + print("-" * len(header))
> + for params in test_parameters:
> + print(f"{params['frame_size']:>12} |
> {params['num_descriptors']:>12} | ", end="")
> + print(f"{params['measured_mpps']:>12.2f} |
> {params['expected_mpps']:>14.2f}")
> + print("-" * len(header))
> +
> + write_performance_json({"results": test_parameters})
> +
> + @perf_test
> + def single_core_forward_perf(self) -> None:
> + """Validate expected single core forwarding performance.
> +
> + Steps:
> + * Create a packet according to the frame size specified in
> the test config.
> + * Transmit from the traffic generator's ports 0 and 1 at
> above the expect.
> + * Forward on TestPMD's interfaces 0 and 1 with 1 core.
> +
> + Verify:
> + * The resulting MPPS forwarded is greater than
> expected_mpps*(1-delta_tolerance).
> + """
> + # Find SUT DPDK driver to determine driver specific performance
> optimization flags
> + sut_dpdk_driver =
> self._ctx.sut_node.config.ports[0].os_driver_for_dpdk
> +
> + for params in self.test_parameters:
> + frame_size = params["frame_size"]
> + num_descriptors = params["num_descriptors"]
> +
> + driver_specific_testpmd_args: TestPmdParamsDict = {
> + "tx_ring": TXRingParams(descriptors=num_descriptors),
> + "rx_ring": RXRingParams(descriptors=num_descriptors),
> + "nb_cores": 1,
> + }
> +
> + if sut_dpdk_driver == "mlx5_core":
> + driver_specific_testpmd_args["burst"] = 64
> + driver_specific_testpmd_args["mbcache"] = 512
> + elif sut_dpdk_driver == "i40e":
> + driver_specific_testpmd_args["rx_queues"] = 2
> + driver_specific_testpmd_args["tx_queues"] = 2
> +
> + with TestPmd(
> + **driver_specific_testpmd_args,
> + ) as testpmd:
> + params["measured_mpps"] = self._transmit(testpmd,
> frame_size)
> + params["performance_delta"] = (
> + float(params["measured_mpps"]) -
> float(params["expected_mpps"])
> + ) / float(params["expected_mpps"])
> + params["pass"] = float(params["performance_delta"]) >=
> -self.delta_tolerance
> +
> + self._produce_stats_table(self.test_parameters)
> +
> + for params in self.test_parameters:
> + verify(
> + params["pass"] is True,
> + f"""Packets forwarded is less than {(1
> -self.delta_tolerance)*100}%
> + of the expected baseline.
> + Measured MPPS = {params["measured_mpps"]}
> + Expected MPPS = {params["expected_mpps"]}""",
> + )
> --
> 2.49.0
>
>
[-- Attachment #2: Type: text/html, Size: 18113 bytes --]
next prev parent reply other threads:[~2025-11-06 13:30 UTC|newest]
Thread overview: 57+ messages / expand[flat|nested] mbox.gz Atom feed top
2025-04-23 19:40 [RFC Patch v1 0/5] Add TREX Traffic Generator to DTS Framework Nicholas Pratte
2025-04-23 19:40 ` [RFC Patch v1 1/5] dts: rework config module to support perf TGs Nicholas Pratte
2025-04-23 19:40 ` [RFC Patch v1 2/5] dts: rework traffic generator inheritance structure Nicholas Pratte
2025-05-15 19:24 ` Patrick Robb
2025-05-16 19:12 ` Nicholas Pratte
2025-04-23 19:40 ` [RFC Patch v1 3/5] dts: add asychronous support to ssh sessions Nicholas Pratte
2025-05-15 19:24 ` Patrick Robb
2025-04-23 19:40 ` [RFC Patch v1 4/5] dts: add trex traffic generator to dts framework Nicholas Pratte
2025-05-15 19:25 ` Patrick Robb
2025-05-16 19:45 ` Nicholas Pratte
2025-04-23 19:40 ` [RFC Patch v1 5/5] dts: add performance test functions to test suite api Nicholas Pratte
2025-05-15 19:25 ` Patrick Robb
2025-05-16 20:18 ` [RFC v2 0/6] Add TREX Traffic Generator to DTS Framework Nicholas Pratte
2025-05-16 20:18 ` [RFC v2 1/6] dts: rework config module to support perf TGs Nicholas Pratte
2025-05-20 20:33 ` Dean Marx
2025-05-16 20:18 ` [RFC v2 2/6] dts: rework traffic generator inheritance structure Nicholas Pratte
2025-05-21 20:36 ` Dean Marx
2025-05-16 20:18 ` [RFC v2 3/6] dts: add asynchronous support to ssh sessions Nicholas Pratte
2025-05-22 15:04 ` Dean Marx
2025-05-16 20:18 ` [RFC v2 4/6] dts: add extended timeout option to interactive shells Nicholas Pratte
2025-05-22 15:10 ` Dean Marx
2025-05-16 20:18 ` [RFC v2 5/6] dts: add trex traffic generator to dts framework Nicholas Pratte
2025-05-22 16:55 ` Dean Marx
2025-05-16 20:18 ` [RFC v2 6/6] dts: add performance test functions to test suite api Nicholas Pratte
2025-05-22 17:54 ` Dean Marx
2025-07-02 5:21 ` [PATCH v3 1/5] dts: rework config module to support perf TGs Patrick Robb
2025-07-02 5:21 ` [PATCH v3 2/5] dts: rework traffic generator inheritance structure Patrick Robb
2025-07-02 15:31 ` Luca Vizzarro
2025-07-02 5:21 ` [PATCH v3 3/5] dts: add timeout override option to interactive shells Patrick Robb
2025-07-02 15:33 ` Luca Vizzarro
2025-07-02 5:21 ` [PATCH v3 4/5] dts: add trex traffic generator to dts framework Patrick Robb
2025-07-02 16:32 ` Luca Vizzarro
2025-07-02 5:21 ` [PATCH v3 5/5] dts: add performance test functions to test suite API Patrick Robb
2025-07-02 16:37 ` Luca Vizzarro
2025-07-02 15:09 ` [PATCH v3 1/5] dts: rework config module to support perf TGs Luca Vizzarro
2025-10-01 23:16 ` [PATCH v4 0/3] Add TREX Traffic Generator to DTS Framework Patrick Robb
2025-10-01 23:16 ` [PATCH v4 1/3] dts: rework traffic generator inheritance structure Patrick Robb
2025-10-08 11:45 ` Luca Vizzarro
2025-10-08 16:34 ` Patrick Robb
2025-10-01 23:16 ` [PATCH v4 2/3] dts: add trex traffic generator to dts framework Patrick Robb
2025-10-01 23:16 ` [PATCH v4 3/3] dts: add performance test functions to test suite API Patrick Robb
2025-10-10 18:41 ` Dean Marx
2025-10-23 1:30 ` [PATCH v5 0/3] Add TREX Traffic Generator to DTS Framework Patrick Robb
2025-10-23 1:30 ` [PATCH v5 1/3] dts: rework traffic generator inheritance structure Patrick Robb
2025-10-30 14:41 ` Andrew Bailey
2025-10-23 1:30 ` [PATCH v5 2/3] dts: add trex traffic generator to dts framework Patrick Robb
2025-10-30 14:46 ` Andrew Bailey
2025-10-23 1:30 ` [PATCH v5 3/3] dts: add performance test functions to test suite API Patrick Robb
2025-10-30 14:53 ` Andrew Bailey
2025-11-05 22:36 ` [PATCH v6 0/3] Add TREX Traffic Generator to DTS Framework Patrick Robb
2025-11-05 22:36 ` [PATCH v6 1/3] dts: rework traffic generator inheritance structure Patrick Robb
2025-11-06 13:37 ` Andrew Bailey
2025-11-05 22:36 ` [PATCH v6 2/3] dts: add trex traffic generator to dts framework Patrick Robb
2025-11-06 13:33 ` Andrew Bailey
2025-11-05 22:36 ` [PATCH v6 3/3] dts: add performance test functions to test suite API Patrick Robb
2025-11-06 13:30 ` Andrew Bailey [this message]
2025-11-06 15:25 ` Andrew Bailey
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=CABJ3N2WsSuKhij2XtU1vo6E+dsuGyf9p0sQvaDM1hEhRcxU6eQ@mail.gmail.com \
--to=abailey@iol.unh.edu \
--cc=Luca.Vizzarro@arm.com \
--cc=Paul.Szczepanek@arm.com \
--cc=dev@dpdk.org \
--cc=dmarx@iol.unh.edu \
--cc=npratte@iol.unh.edu \
--cc=probb@iol.unh.edu \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).