DPDK patches and discussions
 help / color / mirror / Atom feed
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 --]

      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).