From: Stephen Hemminger <stephen@networkplumber.org>
To: dev@dpdk.org
Cc: Stephen Hemminger <stephen@networkplumber.org>,
Reshma Pattan <reshma.pattan@intel.com>
Subject: [PATCH v7 4/7] pcapng: improve performance of timestamping
Date: Fri, 13 Feb 2026 11:18:21 -0800 [thread overview]
Message-ID: <20260213192039.221213-5-stephen@networkplumber.org> (raw)
In-Reply-To: <20260213192039.221213-1-stephen@networkplumber.org>
Avoid doing expensive divide operations when converting
timestamps from cycles (TSC) to nanoseconds for pcapng.
Precompute a rte_reciprocal_u64 inverse of the TSC frequency
and a right-shift count chosen so that the intermediate
product (delta >> shift) * NSEC_PER_SEC cannot overflow
uint64_t. The per-packet conversion then requires only a
shift, a multiply, and a reciprocal divide—no division.
For TSC frequencies less than 18.4 GHz the shift value will
be zero but code is defensive to be future proof.
Signed-off-by: Stephen Hemminger <stephen@networkplumber.org>
---
lib/pcapng/rte_pcapng.c | 97 +++++++++++++++++++++++++++++++----------
1 file changed, 73 insertions(+), 24 deletions(-)
diff --git a/lib/pcapng/rte_pcapng.c b/lib/pcapng/rte_pcapng.c
index a2254ba807..7eedbaf298 100644
--- a/lib/pcapng/rte_pcapng.c
+++ b/lib/pcapng/rte_pcapng.c
@@ -37,12 +37,23 @@
/* upper bound for strings in pcapng option data */
#define PCAPNG_STR_MAX UINT16_MAX
+/*
+ * Converter from TSC values to nanoseconds since Unix epoch.
+ * Uses reciprocal multiply to avoid runtime division.
+ */
+struct tsc_clock {
+ uint64_t tsc_base; /* TSC value at initialization. */
+ uint64_t ns_base; /* Nanoseconds since epoch at init. */
+ struct rte_reciprocal_u64 tsc_hz_inv; /* Reciprocal of TSC frequency. */
+ uint32_t shift; /* Pre-shift to avoid overflow. */
+};
+
/* Format of the capture file handle */
struct rte_pcapng {
int outfd; /* output file */
unsigned int ports; /* number of interfaces added */
- uint64_t offset_ns; /* ns since 1/1/1970 when initialized */
- uint64_t tsc_base; /* TSC when started */
+
+ struct tsc_clock clock;
/* DPDK port id to interface index in file */
uint32_t port_index[RTE_MAX_ETHPORTS];
@@ -98,21 +109,59 @@ static ssize_t writev(int fd, const struct iovec *iov, int iovcnt)
#define if_indextoname(ifindex, ifname) NULL
#endif
-/* Convert from TSC (CPU cycles) to nanoseconds */
-static uint64_t
-pcapng_timestamp(const rte_pcapng_t *self, uint64_t cycles)
+/*
+ * Initialize TSC-to-epoch-ns converter.
+ *
+ * Captures current TSC and system clock as a reference point.
+ */
+static int
+tsc_clock_init(struct tsc_clock *clk)
{
- uint64_t delta, rem, secs, ns;
- const uint64_t hz = rte_get_tsc_hz();
+ struct timespec ts;
+ uint64_t cycles, tsc_hz, divisor;
+ uint32_t shift;
+
+ memset(clk, 0, sizeof(*clk));
+
+ /* If Hz is zero, something is seriously broken. */
+ tsc_hz = rte_get_tsc_hz();
+ if (tsc_hz == 0)
+ return -1;
+
+ /*
+ * Choose shift so (delta >> shift) * NSEC_PER_SEC fits in uint64_t.
+ * For typical GHz-range TSC and ~1s deltas this is 0.
+ */
+ shift = 0;
+ divisor = tsc_hz;
+ while (divisor > UINT64_MAX / NSEC_PER_SEC) {
+ divisor >>= 1;
+ shift++;
+ }
+
+ clk->shift = shift;
+ clk->tsc_hz_inv = rte_reciprocal_value_u64(divisor);
+
+ /* Sample TSC and system clock as close together as possible. */
+ cycles = rte_get_tsc_cycles();
+ clock_gettime(CLOCK_REALTIME, &ts);
+ clk->tsc_base = (cycles + rte_get_tsc_cycles()) / 2;
+ clk->ns_base = (uint64_t)ts.tv_sec * NSEC_PER_SEC + ts.tv_nsec;
+
+ return 0;
+}
- delta = cycles - self->tsc_base;
+/* Convert a TSC value to nanoseconds since Unix epoch. */
+static inline uint64_t
+tsc_to_ns_epoch(const struct tsc_clock *clk, uint64_t tsc)
+{
+ uint64_t delta, ns;
- /* Avoid numeric wraparound by computing seconds first */
- secs = delta / hz;
- rem = delta % hz;
- ns = (rem * NS_PER_S) / hz;
+ delta = tsc - clk->tsc_base;
+ ns = (delta >> clk->shift) * NSEC_PER_SEC;
+ ns = rte_reciprocal_divide_u64(ns, &clk->tsc_hz_inv);
- return secs * NS_PER_S + ns + self->offset_ns;
+ return clk->ns_base + ns;
}
/* length of option including padding */
@@ -344,7 +393,7 @@ rte_pcapng_write_stats(rte_pcapng_t *self, uint16_t port_id,
{
struct pcapng_statistics *hdr;
struct pcapng_option *opt;
- uint64_t start_time = self->offset_ns;
+ uint64_t start_time = self->clock.ns_base;
uint64_t sample_time;
uint32_t optlen, len;
uint32_t *buf;
@@ -397,7 +446,7 @@ rte_pcapng_write_stats(rte_pcapng_t *self, uint16_t port_id,
hdr->block_length = len;
hdr->interface_id = self->port_index[port_id];
- sample_time = pcapng_timestamp(self, rte_get_tsc_cycles());
+ sample_time = tsc_to_ns_epoch(&self->clock, rte_get_tsc_cycles());
hdr->timestamp_hi = sample_time >> 32;
hdr->timestamp_lo = (uint32_t)sample_time;
@@ -684,10 +733,13 @@ rte_pcapng_write_packets(rte_pcapng_t *self,
return -1;
}
- /* adjust timestamp recorded in packet */
+ /*
+ * When data is captured pcapng_copy the current TSC is stored.
+ * Adjust the value recorded in file to PCAP epoch units.
+ */
cycles = (uint64_t)epb->timestamp_hi << 32;
cycles += epb->timestamp_lo;
- timestamp = pcapng_timestamp(self, cycles);
+ timestamp = tsc_to_ns_epoch(&self->clock, cycles);
epb->timestamp_hi = timestamp >> 32;
epb->timestamp_lo = (uint32_t)timestamp;
@@ -733,8 +785,6 @@ rte_pcapng_fdopen(int fd,
{
unsigned int i;
rte_pcapng_t *self;
- struct timespec ts;
- uint64_t cycles;
int ret;
if ((osname && strlen(osname) > PCAPNG_STR_MAX) ||
@@ -754,11 +804,10 @@ rte_pcapng_fdopen(int fd,
self->outfd = fd;
self->ports = 0;
- /* record start time in ns since 1/1/1970 */
- cycles = rte_get_tsc_cycles();
- clock_gettime(CLOCK_REALTIME, &ts);
- self->tsc_base = (cycles + rte_get_tsc_cycles()) / 2;
- self->offset_ns = rte_timespec_to_ns(&ts);
+ if (tsc_clock_init(&self->clock) < 0) {
+ rte_errno = ENODEV;
+ goto fail;
+ }
for (i = 0; i < RTE_MAX_ETHPORTS; i++)
self->port_index[i] = UINT32_MAX;
--
2.51.0
next prev parent reply other threads:[~2026-02-13 19:21 UTC|newest]
Thread overview: 58+ messages / expand[flat|nested] mbox.gz Atom feed top
2025-11-26 5:12 [RFC] pcapng: improve performance of timestamping Stephen Hemminger
2025-12-29 23:01 ` [PATCH v2 0/6] pcapng: timestamping and comment fixes Stephen Hemminger
2025-12-29 23:01 ` [PATCH v2 1/6] pcapng: use alloca instead of fixed buffer Stephen Hemminger
2025-12-29 23:01 ` [PATCH v2 2/6] pcapng: add additional mbuf if space required on copy Stephen Hemminger
2025-12-29 23:01 ` [PATCH v2 3/6] test: add more tests for comments in pcapng Stephen Hemminger
2025-12-29 23:01 ` [PATCH v2 4/6] test: vary size of packets in pcapng test Stephen Hemminger
2025-12-29 23:01 ` [PATCH v2 5/6] test: increase gap " Stephen Hemminger
2025-12-29 23:01 ` [PATCH v2 6/6] pcapng: improve performance of timestamping Stephen Hemminger
2026-01-12 4:50 ` [PATCH v3 0/7] pcapng: fixes and improvements Stephen Hemminger
2026-01-12 4:50 ` [PATCH v3 1/7] pcapng: add length checks to string arguments Stephen Hemminger
2026-01-12 4:50 ` [PATCH v3 2/7] pcapng: use malloc instead of fixed buffer size Stephen Hemminger
2026-01-12 4:50 ` [PATCH v3 3/7] pcapng: add additional mbuf if space required on copy Stephen Hemminger
2026-01-12 4:50 ` [PATCH v3 4/7] test: add more tests for comments in pcapng Stephen Hemminger
2026-01-12 4:50 ` [PATCH v3 5/7] test: vary size of packets in pcapng test Stephen Hemminger
2026-01-12 4:50 ` [PATCH v3 6/7] test: increase gap " Stephen Hemminger
2026-01-12 4:50 ` [PATCH v3 7/7] pcapng: improve performance of timestamping Stephen Hemminger
2026-01-13 0:51 ` [PATCH v4 0/7] pcapng: fixes and improvements Stephen Hemminger
2026-01-13 0:51 ` [PATCH v4 1/7] pcapng: add length checks to string arguments Stephen Hemminger
2026-01-13 0:51 ` [PATCH v4 2/7] pcapng: use malloc instead of fixed buffer size Stephen Hemminger
2026-01-13 0:51 ` [PATCH v4 3/7] pcapng: add additional mbuf if space required on copy Stephen Hemminger
2026-01-13 0:51 ` [PATCH v4 4/7] test: add more tests for comments in pcapng Stephen Hemminger
2026-01-13 0:51 ` [PATCH v4 5/7] test: vary size of packets in pcapng test Stephen Hemminger
2026-01-13 0:51 ` [PATCH v4 6/7] test: increase gap " Stephen Hemminger
2026-01-13 0:51 ` [PATCH v4 7/7] pcapng: improve performance of timestamping Stephen Hemminger
2026-01-19 18:18 ` [PATCH v5 0/5] pcapng: fixes and improvements Stephen Hemminger
2026-01-19 18:18 ` [PATCH v5 1/5] pcapng: add length checks to string arguments Stephen Hemminger
2026-01-19 18:19 ` [PATCH v5 2/5] pcapng: use malloc instead of fixed buffer size Stephen Hemminger
2026-01-19 18:19 ` [PATCH v5 3/5] pcapng: add additional mbuf if space required on copy Stephen Hemminger
2026-01-19 18:19 ` [PATCH v5 4/5] pcapng: improve performance of timestamping Stephen Hemminger
2026-01-19 18:19 ` [PATCH v5 5/5] test: add more tests for pcapng Stephen Hemminger
2026-01-26 21:04 ` [PATCH v6 0/5] pcapng: fixes and improvements Stephen Hemminger
2026-01-26 21:04 ` [PATCH v6 1/5] pcapng: add length checks to string arguments Stephen Hemminger
2026-01-26 21:04 ` [PATCH v6 2/5] pcapng: use malloc instead of fixed buffer size Stephen Hemminger
2026-01-26 21:04 ` [PATCH v6 3/5] pcapng: chain additional mbuf when comment exceeds tailroom Stephen Hemminger
2026-01-26 21:04 ` [PATCH v6 4/5] pcapng: improve performance of timestamping Stephen Hemminger
2026-01-26 21:04 ` [PATCH v6 5/5] test/pcapng: add tests for comments Stephen Hemminger
2026-02-13 19:18 ` [PATCH v7 0/7] pcapng: fixes and improvements Stephen Hemminger
2026-02-13 19:18 ` [PATCH v7 1/7] pcapng: add length checks to string arguments Stephen Hemminger
2026-02-13 19:18 ` [PATCH v7 2/7] pcapng: use malloc instead of fixed buffer size Stephen Hemminger
2026-02-13 19:18 ` [PATCH v7 3/7] pcapng: chain additional mbuf when comment exceeds tailroom Stephen Hemminger
2026-02-13 19:18 ` Stephen Hemminger [this message]
2026-02-13 19:18 ` [PATCH v7 5/7] test/pcapng: add tests for comments Stephen Hemminger
2026-02-13 19:18 ` [PATCH v7 6/7] test/pcapng: skip test if null driver missing Stephen Hemminger
2026-02-16 10:01 ` David Marchand
2026-02-16 16:26 ` Stephen Hemminger
2026-02-16 16:43 ` David Marchand
2026-02-13 19:18 ` [PATCH v7 7/7] dumpcap: improve pcapng error reporting Stephen Hemminger
2026-02-16 21:37 ` [PATCH v8 0/8] pcapng: fixes and improvements Stephen Hemminger
2026-02-16 21:37 ` [PATCH v8 1/8] pcapng: correct typo in comment Stephen Hemminger
2026-02-16 21:37 ` [PATCH v8 2/8] pcapng: document return values Stephen Hemminger
2026-02-16 21:38 ` [PATCH v8 3/8] pcapng: add length checks to string arguments Stephen Hemminger
2026-02-17 14:34 ` Thomas Monjalon
2026-02-16 21:38 ` [PATCH v8 4/8] pcapng: use malloc instead of fixed buffer size Stephen Hemminger
2026-02-16 21:38 ` [PATCH v8 5/8] pcapng: chain additional mbuf when comment exceeds tailroom Stephen Hemminger
2026-02-16 21:38 ` [PATCH v8 6/8] pcapng: improve performance of timestamping Stephen Hemminger
2026-02-16 21:38 ` [PATCH v8 7/8] test/pcapng: skip test if null driver missing Stephen Hemminger
2026-02-16 21:38 ` [PATCH v8 8/8] test/pcapng: add tests for comments Stephen Hemminger
2026-02-17 16:39 ` [PATCH v8 0/8] pcapng: fixes and improvements Thomas Monjalon
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=20260213192039.221213-5-stephen@networkplumber.org \
--to=stephen@networkplumber.org \
--cc=dev@dpdk.org \
--cc=reshma.pattan@intel.com \
/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