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 10:25:48 -0500 [thread overview]
Message-ID: <CABJ3N2XqjAjJxOqk_5ZTp8+sah+Si0mvRwPO+E0SL+YYP_HtJw@mail.gmail.com> (raw)
In-Reply-To: <CABJ3N2WsSuKhij2XtU1vo6E+dsuGyf9p0sQvaDM1hEhRcxU6eQ@mail.gmail.com>
[-- Attachment #1: Type: text/plain, Size: 16530 bytes --]
+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
Disregard the last comment, I had assumed that the delta_tolerance was in
mpps
not percentage of expected_mpps.
nit: It may be helpful to declare this distinction in a comment above the
delta_tolerance
variable or perhaps use percent_tolerance instead.
Reviewed-by: Andrew Bailey <abailey@iol.unh.edu>
On Thu, Nov 6, 2025 at 8:30 AM Andrew Bailey <abailey@iol.unh.edu> wrote:
> + 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: 19778 bytes --]
prev parent reply other threads:[~2025-11-06 15:26 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
2025-11-06 15:25 ` Andrew Bailey [this message]
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=CABJ3N2XqjAjJxOqk_5ZTp8+sah+Si0mvRwPO+E0SL+YYP_HtJw@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).