public inbox for dev@dpdk.org
 help / color / mirror / Atom feed
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


  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