[PATCH 3/3] resolv: Optimize inet_ntop

Adhemerval Zanella adhemerval.zanella@linaro.org
Tue Jun 3 19:51:54 GMT 2025


The benchtests/inet_ntop_ipv4 and benchtests/inet_ntop_ipv6 profile
shows that most of time is spent in costly sprint operations:

$ perf record ./benchtests/bench-inet_ntop_ipv4 && perf report --stdio
[...]
    38.53%  bench-inet_ntop  libc.so               [.] __printf_buffer
    18.69%  bench-inet_ntop  libc.so               [.] __printf_buffer_write
    11.01%  bench-inet_ntop  libc.so               [.] _itoa_word
     8.02%  bench-inet_ntop  bench-inet_ntop_ipv4  [.] bench_start
     6.99%  bench-inet_ntop  libc.so               [.] __memmove_avx_unaligned_erms
     3.86%  bench-inet_ntop  libc.so               [.] __strchrnul_avx2
     2.82%  bench-inet_ntop  libc.so               [.] __strcpy_avx2
     1.90%  bench-inet_ntop  libc.so               [.] inet_ntop4
     1.78%  bench-inet_ntop  libc.so               [.] __vsprintf_internal
     1.55%  bench-inet_ntop  libc.so               [.] __sprintf_chk
     1.18%  bench-inet_ntop  libc.so               [.] __GI___inet_ntop

$ perf record ./benchtests/bench-inet_ntop_ipv6 && perf report --stdio
    35.44%  bench-inet_ntop  libc.so               [.] __printf_buffer
    14.35%  bench-inet_ntop  libc.so               [.] __printf_buffer_write
    10.27%  bench-inet_ntop  libc.so               [.] __GI___inet_ntop
     7.93%  bench-inet_ntop  libc.so               [.] _itoa_word
     7.00%  bench-inet_ntop  libc.so               [.] __sprintf_chk
     6.20%  bench-inet_ntop  libc.so               [.] __vsprintf_internal
     5.26%  bench-inet_ntop  libc.so               [.] __strchrnul_avx2
     5.05%  bench-inet_ntop  bench-inet_ntop_ipv6  [.] bench_start
     3.70%  bench-inet_ntop  libc.so               [.] __memmove_avx_unaligned_erms
     2.11%  bench-inet_ntop  libc.so               [.] __printf_buffer_done

The printf usage is replaced with an expanded function that prints
either an IPv4 octet or and IPv6 quartet, the strcpy is replaced
with a memcpy (since ABIs usually optimizes the symbol), and
inline is used for both inet_ntop4 and inet_ntop6.

The performance results on aarch64 Neoverse1 with gcc 14.2.1:

* master

aarch64-linux-gnu-master$ ./benchtests/bench-inet_ntop_ipv4
  "inet_ntop_ipv4": {
   "workload-ipv4-random": {
    "duration": 1.43067e+09,
    "iterations": 8e+06,
    "reciprocal-throughput": 178.572,
    "latency": 179.096,
    "max-throughput": 5.59997e+06,
    "min-throughput": 5.58359e+06
   }
aarch64-linux-gnu-master$ ./benchtests/bench-inet_ntop_ipv6
  "inet_ntop_ipv6": {
   "workload-ipv6-random": {
    "duration": 1.68539e+09,
    "iterations": 4e+06,
    "reciprocal-throughput": 421.307,
    "latency": 421.388,
    "max-throughput": 2.37357e+06,
    "min-throughput": 2.37311e+06
   }
  }

* patched

aarch64-linux-gnu$ ./benchtests/bench-inet_ntop_ipv4
  "inet_ntop_ipv4": {
   "workload-ipv4-random": {
    "duration": 1.06324e+09,
    "iterations": 4.4e+07,
    "reciprocal-throughput": 24.1509,
    "latency": 24.178,
    "max-throughput": 4.14063e+07,
    "min-throughput": 4.13599e+07
   }
aarch64-linux-gnu$ ./benchtests/bench-inet_ntop_ipv6
  "inet_ntop_ipv6": {
   "workload-ipv6-random": {
    "duration": 1.08476e+09,
    "iterations": 2.4e+07,
    "reciprocal-throughput": 45.2794,
    "latency": 45.1174,
    "max-throughput": 2.20851e+07,
    "min-throughput": 2.21644e+07
   }
  }

Checked on aarch64-linux-gnu and x86_64-linux-gnu.
---
 resolv/inet_ntop.c | 134 +++++++++++++++++++++++++++++----------------
 1 file changed, 87 insertions(+), 47 deletions(-)

diff --git a/resolv/inet_ntop.c b/resolv/inet_ntop.c
index acf5f3cb88..3f2a9ddd87 100644
--- a/resolv/inet_ntop.c
+++ b/resolv/inet_ntop.c
@@ -26,45 +26,44 @@
 #include <errno.h>
 #include <stdio.h>
 #include <string.h>
-
-#ifdef SPRINTF_CHAR
-# define SPRINTF(x) strlen(sprintf/**/x)
-#else
-# define SPRINTF(x) ((size_t)sprintf x)
-#endif
+#include <intprops.h>
+#include <_itoa.h>
 
 /*
  * WARNING: Don't even consider trying to compile this on a system where
  * sizeof(int) < 4.  sizeof(int) > 4 is fine; all the world's not a VAX.
  */
 
-static const char *inet_ntop4 (const u_char *src, char *dst, socklen_t size);
-static const char *inet_ntop6 (const u_char *src, char *dst, socklen_t size);
-
-/* char *
- * __inet_ntop(af, src, dst, size)
- *	convert a network format address to presentation format.
- * return:
- *	pointer to presentation format address (`dst'), or NULL (see errno).
- * author:
- *	Paul Vixie, 1996.
- */
-const char *
-__inet_ntop (int af, const void *src, char *dst, socklen_t size)
+static inline char *put_uint8 (uint8_t word, char *tp)
 {
-	switch (af) {
-	case AF_INET:
-		return (inet_ntop4(src, dst, size));
-	case AF_INET6:
-		return (inet_ntop6(src, dst, size));
-	default:
-		__set_errno (EAFNOSUPPORT);
-		return (NULL);
-	}
-	/* NOTREACHED */
+  intptr_t s = 1;
+  if (word >= 100)
+    {
+      tp[2] = _itoa_lower_digits[word % 10];
+      word /= 10;
+      s += 1;
+    }
+  if (word >= 10)
+    {
+      tp[1] = _itoa_lower_digits[word % 10];
+      word /= 10;
+      s += 1;
+    }
+  *tp = _itoa_lower_digits[word % 10];
+  return tp + s;
+}
+
+static inline char *put_uint16 (uint16_t word, char *tp)
+{
+  if (word >= 0x1000)
+    *tp++ = _itoa_lower_digits[(word >> 12) & 0xf];
+  if (word >= 0x100)
+    *tp++ = _itoa_lower_digits[(word >> 8) & 0xf];
+  if (word >= 0x10)
+    *tp++ = _itoa_lower_digits[(word >> 4) & 0xf];
+  *tp++ = _itoa_lower_digits[word & 0xf];
+  return tp;
 }
-libc_hidden_def (__inet_ntop)
-weak_alias (__inet_ntop, inet_ntop)
 
 /* const char *
  * inet_ntop4(src, dst, size)
@@ -74,20 +73,36 @@ weak_alias (__inet_ntop, inet_ntop)
  * notes:
  *	(1) uses no statics
  *	(2) takes a u_char* not an in_addr as input
- * author:
- *	Paul Vixie, 1996.
  */
-static const char *
+static __always_inline const char *
 inet_ntop4 (const u_char *src, char *dst, socklen_t size)
 {
-	static const char fmt[] = "%u.%u.%u.%u";
-	char tmp[sizeof "255.255.255.255"];
+  enum
+    {
+      oct_size = INT_BUFSIZE_BOUND (u_char),
+      tmp_size = 4 * oct_size + 3 /* '.' */ + 1 /* '\0' */
+    };
+  char tmp[tmp_size];
+  char *tmp_r = tmp;
 
-	if (SPRINTF((tmp, fmt, src[0], src[1], src[2], src[3])) >= size) {
-		__set_errno (ENOSPC);
-		return (NULL);
-	}
-	return strcpy(dst, tmp);
+  tmp_r = put_uint8 (src[0], tmp_r);
+  *(tmp_r++) = '.';
+  tmp_r = put_uint8 (src[1], tmp_r);
+  *(tmp_r++) = '.';
+  tmp_r = put_uint8 (src[2], tmp_r);
+  *(tmp_r++) = '.';
+  tmp_r = put_uint8 (src[3], tmp_r);
+  *tmp_r++ = '\0';
+
+  socklen_t tmp_s = tmp_r - tmp;
+  if (tmp_s > size)
+    {
+      __set_errno (ENOSPC);
+      return NULL;
+    }
+  memcpy (dst, tmp, tmp_s);
+
+  return dst;
 }
 
 /* const char *
@@ -96,7 +111,7 @@ inet_ntop4 (const u_char *src, char *dst, socklen_t size)
  * author:
  *	Paul Vixie, 1996.
  */
-static const char *
+static inline const char *
 inet_ntop6 (const u_char *src, char *dst, socklen_t size)
 {
 	/*
@@ -108,7 +123,7 @@ inet_ntop6 (const u_char *src, char *dst, socklen_t size)
 	 */
 	char tmp[sizeof "ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255"], *tp;
 	struct { int base, len; } best, cur;
-	u_int words[NS_IN6ADDRSZ / NS_INT16SZ];
+	uint16_t words[NS_IN6ADDRSZ / NS_INT16SZ] = { 0 };
 	int i;
 
 	/*
@@ -116,7 +131,6 @@ inet_ntop6 (const u_char *src, char *dst, socklen_t size)
 	 *	Copy the input (bytewise) array into a wordwise array.
 	 *	Find the longest run of 0x00's in src[] for :: shorthanding.
 	 */
-	memset(words, '\0', sizeof words);
 	for (i = 0; i < NS_IN6ADDRSZ; i += 2)
 		words[i / 2] = (src[i] << 8) | src[i + 1];
 	best.base = -1;
@@ -167,7 +181,7 @@ inet_ntop6 (const u_char *src, char *dst, socklen_t size)
 			tp += strlen(tp);
 			break;
 		}
-		tp += SPRINTF((tp, "%x", words[i]));
+		tp = put_uint16 (words[i], tp);
 	}
 	/* Was it a trailing run of 0x00's? */
 	if (best.base != -1 && (best.base + best.len) ==
@@ -178,9 +192,35 @@ inet_ntop6 (const u_char *src, char *dst, socklen_t size)
 	/*
 	 * Check for overflow, copy, and we're done.
 	 */
-	if ((socklen_t)(tp - tmp) > size) {
+	socklen_t tmp_s = tp - tmp;
+	if (tmp_s > size) {
 		__set_errno (ENOSPC);
 		return (NULL);
 	}
-	return strcpy(dst, tmp);
+	return memcpy(dst, tmp, tmp_s);
 }
+
+/* char *
+ * __inet_ntop(af, src, dst, size)
+ *	convert a network format address to presentation format.
+ * return:
+ *	pointer to presentation format address (`dst'), or NULL (see errno).
+ * author:
+ *	Paul Vixie, 1996.
+ */
+const char *
+__inet_ntop (int af, const void *src, char *dst, socklen_t size)
+{
+	switch (af) {
+	case AF_INET:
+		return (inet_ntop4(src, dst, size));
+	case AF_INET6:
+		return (inet_ntop6(src, dst, size));
+	default:
+		__set_errno (EAFNOSUPPORT);
+		return (NULL);
+	}
+	/* NOTREACHED */
+}
+libc_hidden_def (__inet_ntop)
+weak_alias (__inet_ntop, inet_ntop)
-- 
2.43.0



More information about the Libc-alpha mailing list