From cd8e7981e08dace480fd340646e2504301d5d96c Mon Sep 17 00:00:00 2001 From: vdimir Date: Sun, 29 Nov 2020 20:54:46 +0300 Subject: [PATCH 1/3] Speedup applyCIDRMask for IPv6 with compile-time generated mask array --- src/Common/IPv6ToBinary.cpp | 33 +++++++++++++++++++++++++++++++ src/Common/IPv6ToBinary.h | 5 +++++ src/Functions/FunctionsCoding.h | 35 ++++++++++++++++++++++----------- 3 files changed, 61 insertions(+), 12 deletions(-) diff --git a/src/Common/IPv6ToBinary.cpp b/src/Common/IPv6ToBinary.cpp index 1fd2e3312f6..da213bcb5a9 100644 --- a/src/Common/IPv6ToBinary.cpp +++ b/src/Common/IPv6ToBinary.cpp @@ -2,12 +2,18 @@ #include #include +#include + #include namespace DB { +constexpr size_t IPV6_MASKS_COUNT = 256; + +using RawMaskArray = std::array; + void IPv6ToRawBinary(const Poco::Net::IPAddress & address, char * res) { if (Poco::Net::IPAddress::IPv6 == address.family()) @@ -33,4 +39,31 @@ std::array IPv6ToBinary(const Poco::Net::IPAddress & address) return res; } +static constexpr RawMaskArray generateBitMask(size_t prefix) { + if (prefix >= 128) + prefix = 128; + RawMaskArray arr{0}; + size_t i = 0; + for (; prefix >= 8; ++i, prefix -= 8) + arr[i] = 0xff; + if (prefix > 0) + arr[i++] = ~(0xff >> prefix); + while (i < 16) + arr[i++] = 0x00; + return arr; +} + +static constexpr std::array generateBitMasks() { + std::array arr{}; + for (size_t i = 0; i < IPV6_MASKS_COUNT; ++i) + arr[i] = generateBitMask(i); + return arr; +} + +const uint8_t * getCIDRMaskIPv6(UInt8 prefix_len) +{ + static constexpr std::array IPV6_RAW_MASK_ARRAY = generateBitMasks(); + return IPV6_RAW_MASK_ARRAY[prefix_len].data(); +} + } diff --git a/src/Common/IPv6ToBinary.h b/src/Common/IPv6ToBinary.h index 2d0d4a20ecb..2e47238aeba 100644 --- a/src/Common/IPv6ToBinary.h +++ b/src/Common/IPv6ToBinary.h @@ -14,4 +14,9 @@ void IPv6ToRawBinary(const Poco::Net::IPAddress & address, char * res); /// Convert IP address to 16-byte array with IPv6 data (big endian). If it's an IPv4, map it to IPv6. std::array IPv6ToBinary(const Poco::Net::IPAddress & address); +/// Returns pointer to 16-byte array containing mask with first `prefix_len` bits set to `1` and `128 - prefix_len` to `0`. +/// Pointer is valid during all program execution time and doesn't require freeing. +/// Values of prefix_len greater than 128 interpreted as 128 exactly. +const uint8_t * getCIDRMaskIPv6(UInt8 prefix_len); + } diff --git a/src/Functions/FunctionsCoding.h b/src/Functions/FunctionsCoding.h index ac3262f5131..e07df450206 100644 --- a/src/Functions/FunctionsCoding.h +++ b/src/Functions/FunctionsCoding.h @@ -1,7 +1,8 @@ #pragma once -#include #include +#include +#include #include #include #include @@ -1617,20 +1618,28 @@ public: class FunctionIPv6CIDRToRange : public IFunction { private: - /// TODO Inefficient. + +#if defined(__SSE2__) + + #include + + static inline void applyCIDRMask(const UInt8 * __restrict src, UInt8 * __restrict dst_lower, UInt8 * __restrict dst_upper, UInt8 bits_to_keep) + { + __m128i mask = _mm_loadu_si128(reinterpret_cast(getCIDRMaskIPv6(bits_to_keep))); + __m128i lower = _mm_and_si128(_mm_loadu_si128(reinterpret_cast(src)), mask); + _mm_storeu_si128(reinterpret_cast<__m128i *>(dst_lower), lower); + + __m128i inv_mask = _mm_xor_si128(mask, _mm_cmpeq_epi32(_mm_setzero_si128(), _mm_setzero_si128())); + __m128i upper = _mm_or_si128(lower, inv_mask); + _mm_storeu_si128(reinterpret_cast<__m128i *>(dst_upper), upper); + } + +#else + /// NOTE IPv6 is stored in memory in big endian format that makes some difficulties. static void applyCIDRMask(const UInt8 * __restrict src, UInt8 * __restrict dst_lower, UInt8 * __restrict dst_upper, UInt8 bits_to_keep) { - UInt8 mask[16]{}; - - UInt8 bytes_to_keep = bits_to_keep / 8; - UInt8 bits_to_keep_in_last_byte = bits_to_keep % 8; - - for (size_t i = 0; i < bits_to_keep / 8; ++i) - mask[i] = 0xFFU; - - if (bits_to_keep_in_last_byte) - mask[bytes_to_keep] = 0xFFU << (8 - bits_to_keep_in_last_byte); + const auto * mask = getCIDRMaskIPv6(bits_to_keep); for (size_t i = 0; i < 16; ++i) { @@ -1639,6 +1648,8 @@ private: } } +#endif + public: static constexpr auto name = "IPv6CIDRToRange"; static FunctionPtr create(const Context &) { return std::make_shared(); } From 1aaff75d9ae1b38e74b5e14708bb2168d408301c Mon Sep 17 00:00:00 2001 From: vdimir Date: Tue, 1 Dec 2020 20:38:49 +0300 Subject: [PATCH 2/3] Fix style in IPv6ToBinary.cpp --- src/Common/IPv6ToBinary.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/Common/IPv6ToBinary.cpp b/src/Common/IPv6ToBinary.cpp index da213bcb5a9..94b831fcc35 100644 --- a/src/Common/IPv6ToBinary.cpp +++ b/src/Common/IPv6ToBinary.cpp @@ -39,7 +39,8 @@ std::array IPv6ToBinary(const Poco::Net::IPAddress & address) return res; } -static constexpr RawMaskArray generateBitMask(size_t prefix) { +static constexpr RawMaskArray generateBitMask(size_t prefix) +{ if (prefix >= 128) prefix = 128; RawMaskArray arr{0}; @@ -53,7 +54,8 @@ static constexpr RawMaskArray generateBitMask(size_t prefix) { return arr; } -static constexpr std::array generateBitMasks() { +static constexpr std::array generateBitMasks() +{ std::array arr{}; for (size_t i = 0; i < IPV6_MASKS_COUNT; ++i) arr[i] = generateBitMask(i); From 9ce010e82cbdef46cfbd87d13c8f82cc7cfa4cc7 Mon Sep 17 00:00:00 2001 From: vdimir Date: Tue, 1 Dec 2020 22:12:11 +0300 Subject: [PATCH 3/3] Add comment for IPV6_MASKS_COUNT --- src/Common/IPv6ToBinary.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Common/IPv6ToBinary.cpp b/src/Common/IPv6ToBinary.cpp index 94b831fcc35..3c004a5a84e 100644 --- a/src/Common/IPv6ToBinary.cpp +++ b/src/Common/IPv6ToBinary.cpp @@ -10,6 +10,8 @@ namespace DB { +/// Result array could be indexed with all possible uint8 values without extra check. +/// For values greater than 128 we will store same value as for 128 (all bits set). constexpr size_t IPV6_MASKS_COUNT = 256; using RawMaskArray = std::array;