[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