From: Nick Terrell Date: Tue, 13 Jun 2017 06:12:42 +0000 (-0700) Subject: Add toFullyQualifiedAppend() methods X-Git-Tag: v2017.06.19.00~28 X-Git-Url: http://plrg.eecs.uci.edu/git/?p=folly.git;a=commitdiff_plain;h=5203b6ba7f4b0dc9f3fa090f2d86a27b9edce8ff Add toFullyQualifiedAppend() methods Summary: A common use case for `IPAddress::toFullyQualified()` is to print a `:` string. It is faster to reserve enough space for both beforehand than create 2 strings. Reviewed By: yfeldblum Differential Revision: D5224454 fbshipit-source-id: 4536f89a9d51d39dd9fd970c753ecb8ecced5d22 --- diff --git a/folly/IPAddress.h b/folly/IPAddress.h index 20eddb91..bd6906cf 100644 --- a/folly/IPAddress.h +++ b/folly/IPAddress.h @@ -387,6 +387,12 @@ class IPAddress { : asV6().toFullyQualified(); } + /// Same as toFullyQualified but append to an output string. + void toFullyQualifiedAppend(std::string& out) const { + return isV4() ? asV4().toFullyQualifiedAppend(out) + : asV6().toFullyQualifiedAppend(out); + } + // Address version (4 or 6) uint8_t version() const { return isV4() ? asV4().version() diff --git a/folly/IPAddressV4.cpp b/folly/IPAddressV4.cpp index dfe46695..cd81d6d9 100644 --- a/folly/IPAddressV4.cpp +++ b/folly/IPAddressV4.cpp @@ -241,6 +241,11 @@ string IPAddressV4::str() const { return detail::fastIpv4ToString(addr_.inAddr_); } +// public +void IPAddressV4::toFullyQualifiedAppend(std::string& out) const { + detail::fastIpv4AppendToString(addr_.inAddr_, out); +} + // public string IPAddressV4::toInverseArpaName() const { return sformat( diff --git a/folly/IPAddressV4.h b/folly/IPAddressV4.h index 09496946..2de04f1c 100644 --- a/folly/IPAddressV4.h +++ b/folly/IPAddressV4.h @@ -54,6 +54,10 @@ typedef std::array ByteArray4; */ class IPAddressV4 { public: + // Max size of std::string returned by toFullyQualified. + static constexpr size_t kMaxToFullyQualifiedSize = + 4 /*words*/ * 3 /*max chars per word*/ + 3 /*separators*/; + // returns true iff the input string can be parsed as an ipv4-address static bool validate(StringPiece ip); @@ -212,6 +216,9 @@ class IPAddressV4 { // @see IPAddress#toFullyQualified std::string toFullyQualified() const { return str(); } + // @see IPAddress#toFullyQualifiedAppend + void toFullyQualifiedAppend(std::string& out) const; + // @see IPAddress#version uint8_t version() const { return 4; } diff --git a/folly/IPAddressV6.cpp b/folly/IPAddressV6.cpp index 6f1eb595..038a68f9 100644 --- a/folly/IPAddressV6.cpp +++ b/folly/IPAddressV6.cpp @@ -425,6 +425,11 @@ string IPAddressV6::toFullyQualified() const { return detail::fastIpv6ToString(addr_.in6Addr_); } +// public +void IPAddressV6::toFullyQualifiedAppend(std::string& out) const { + detail::fastIpv6AppendToString(addr_.in6Addr_, out); +} + // public string IPAddressV6::toInverseArpaName() const { constexpr folly::StringPiece lut = "0123456789abcdef"; diff --git a/folly/IPAddressV6.h b/folly/IPAddressV6.h index 21e892a4..2a3880bb 100644 --- a/folly/IPAddressV6.h +++ b/folly/IPAddressV6.h @@ -281,6 +281,9 @@ class IPAddressV6 { // @see IPAddress#toFullyQualified std::string toFullyQualified() const; + // @see IPAddress#toFullyQualifiedAppend + void toFullyQualifiedAppend(std::string& out) const; + std::string toInverseArpaName() const; // @see IPAddress#str diff --git a/folly/detail/IPAddressSource.h b/folly/detail/IPAddressSource.h index 53ae7b6d..7f08f844 100644 --- a/folly/detail/IPAddressSource.h +++ b/folly/detail/IPAddressSource.h @@ -218,9 +218,8 @@ inline void writeIntegerString(IntegralType val, char** buffer) { *buffer = buf; } -inline std::string fastIpv4ToString(const in_addr& inAddr) { +inline size_t fastIpV4ToBufferUnsafe(const in_addr& inAddr, char* str) { const uint8_t* octets = reinterpret_cast(&inAddr.s_addr); - char str[sizeof("255.255.255.255")]; char* buf = str; writeIntegerString(octets[0], &buf); @@ -231,16 +230,25 @@ inline std::string fastIpv4ToString(const in_addr& inAddr) { *(buf++) = '.'; writeIntegerString(octets[3], &buf); - return std::string(str, size_t(buf - str)); + return buf - str; } -inline std::string fastIpv6ToString(const in6_addr& in6Addr) { +inline std::string fastIpv4ToString(const in_addr& inAddr) { + char str[sizeof("255.255.255.255")]; + return std::string(str, fastIpV4ToBufferUnsafe(inAddr, str)); +} + +inline void fastIpv4AppendToString(const in_addr& inAddr, std::string& out) { + char str[sizeof("255.255.255.255")]; + out.append(str, fastIpV4ToBufferUnsafe(inAddr, str)); +} + +inline size_t fastIpv6ToBufferUnsafe(const in6_addr& in6Addr, char* str) { #ifdef _MSC_VER const uint16_t* bytes = reinterpret_cast(&in6Addr.u.Word); #else const uint16_t* bytes = reinterpret_cast(&in6Addr.s6_addr16); #endif - char str[sizeof("2001:0db8:0000:0000:0000:ff00:0042:8329")]; char* buf = str; for (int i = 0; i < 8; ++i) { @@ -255,7 +263,17 @@ inline std::string fastIpv6ToString(const in6_addr& in6Addr) { } } - return std::string(str, size_t(buf - str)); + return buf - str; +} + +inline std::string fastIpv6ToString(const in6_addr& in6Addr) { + char str[sizeof("2001:0db8:0000:0000:0000:ff00:0042:8329")]; + return std::string(str, fastIpv6ToBufferUnsafe(in6Addr, str)); +} + +inline void fastIpv6AppendToString(const in6_addr& in6Addr, std::string& out) { + char str[sizeof("2001:0db8:0000:0000:0000:ff00:0042:8329")]; + out.append(str, fastIpv6ToBufferUnsafe(in6Addr, str)); } } } diff --git a/folly/test/IPAddressBenchmark.cpp b/folly/test/IPAddressBenchmark.cpp index bf3433ce..3099aa7d 100644 --- a/folly/test/IPAddressBenchmark.cpp +++ b/folly/test/IPAddressBenchmark.cpp @@ -14,6 +14,7 @@ * limitations under the License. */ +#include #include #include @@ -46,6 +47,30 @@ BENCHMARK_RELATIVE(ipv4_to_fully_qualified, iters) { BENCHMARK_DRAW_LINE() +BENCHMARK(ipv4_to_fully_qualified_port, iters) { + IPAddressV4 ip("255.255.255.255"); + while (iters--) { + string outputString = to(ip.toFullyQualified(), ':', 65535); + folly::doNotOptimizeAway(outputString); + folly::doNotOptimizeAway(outputString.data()); + } +} + +BENCHMARK_RELATIVE(ipv4_append_to_fully_qualified_port, iters) { + IPAddressV4 ip("255.255.255.255"); + while (iters--) { + string outputString; + outputString.reserve(IPAddressV4::kMaxToFullyQualifiedSize + 1 + 5); + ip.toFullyQualifiedAppend(outputString); + outputString += ':'; + folly::toAppend(65535, &outputString); + folly::doNotOptimizeAway(outputString); + folly::doNotOptimizeAway(outputString.data()); + } +} + +BENCHMARK_DRAW_LINE() + BENCHMARK(ipv6_to_string_inet_ntop, iters) { IPAddressV6 ipv6Addr("F1E0:0ACE:FB94:7ADF:22E8:6DE6:9672:3725"); in6_addr ip = ipv6Addr.toAddr(); @@ -69,15 +94,45 @@ BENCHMARK_RELATIVE(ipv6_to_fully_qualified, iters) { } } +BENCHMARK_DRAW_LINE() + +BENCHMARK(ipv6_to_fully_qualified_port, iters) { + IPAddressV6 ip("F1E0:0ACE:FB94:7ADF:22E8:6DE6:9672:3725"); + while (iters--) { + string outputString = to(ip.toFullyQualified(), ':', 65535); + folly::doNotOptimizeAway(outputString); + folly::doNotOptimizeAway(outputString.data()); + } +} + +BENCHMARK_RELATIVE(ipv6_append_to_fully_qualified_port, iters) { + IPAddressV6 ip("F1E0:0ACE:FB94:7ADF:22E8:6DE6:9672:3725"); + while (iters--) { + string outputString; + outputString.reserve(folly::IPAddressV6::kToFullyQualifiedSize + 1 + 5); + ip.toFullyQualifiedAppend(outputString); + outputString += ':'; + folly::toAppend(65535, &outputString); + folly::doNotOptimizeAway(outputString); + folly::doNotOptimizeAway(outputString.data()); + } +} + // Benchmark results on Intel Xeon CPU E5-2660 @ 2.20GHz // ============================================================================ // folly/test/IPAddressBenchmark.cpp relative time/iter iters/s // ============================================================================ -// ipv4_to_string_inet_ntop 237.87ns 4.20M -// ipv4_to_fully_qualified 362.31% 65.65ns 15.23M +// ipv4_to_string_inet_ntop 227.13ns 4.40M +// ipv4_to_fully_qualified 1418.95% 16.01ns 62.47M +// ---------------------------------------------------------------------------- +// ipv4_to_fully_qualified_port 77.51ns 12.90M +// ipv4_append_to_fully_qualified_port 133.72% 57.96ns 17.25M +// ---------------------------------------------------------------------------- +// ipv6_to_string_inet_ntop 750.53ns 1.33M +// ipv6_to_fully_qualified 608.68% 123.30ns 8.11M // ---------------------------------------------------------------------------- -// ipv6_to_string_inet_ntop 768.60ns 1.30M -// ipv6_to_fully_qualified 821.81% 93.53ns 10.69M +// ipv6_to_fully_qualified_port 150.76ns 6.63M +// ipv6_append_to_fully_qualified_port 178.73% 84.35ns 11.86M // ============================================================================ int main(int argc, char *argv[]) { diff --git a/folly/test/IPAddressTest.cpp b/folly/test/IPAddressTest.cpp index 3dd64e9a..7f1f5a93 100644 --- a/folly/test/IPAddressTest.cpp +++ b/folly/test/IPAddressTest.cpp @@ -461,11 +461,28 @@ TEST(IPAddress, ToFullyQualifiedLocal) { EXPECT_EQ("0000:0000:0000:0000:0000:0000:0000:0001", ip.toFullyQualified()) << ip; } -TEST(IPAddress, ToFullyQualifiedSize) { +TEST(IPAddress, ToFullyQualifiedAppendV6) { + IPAddress ip("2620:0:1cfe:face:b00c::3"); + std::string result; + ip.toFullyQualifiedAppend(result); + EXPECT_EQ("2620:0000:1cfe:face:b00c:0000:0000:0003", result) << ip; +} +TEST(IPAddress, ToFullyQualifiedAppendV4) { + IPAddress ip("127.0.0.1"); + std::string result; + ip.toFullyQualifiedAppend(result); + EXPECT_EQ("127.0.0.1", result) << ip; +} +TEST(IPAddress, ToFullyQualifiedSizeV6) { auto actual = IPAddressV6::kToFullyQualifiedSize; auto expected = IPAddress("::").toFullyQualified().size(); EXPECT_EQ(expected, actual); } +TEST(IPAddress, MaxToFullyQualifiedSizeV4) { + auto actual = IPAddressV4::kMaxToFullyQualifiedSize; + auto expected = IPAddress("255.255.255.255").toFullyQualified().size(); + EXPECT_EQ(expected, actual); +} // test v4-v6 mapped addresses TEST_P(IPAddressMappedTest, MappedEqual) {