Commit 96000632 authored by Yedidya Feldblum's avatar Yedidya Feldblum Committed by facebook-github-bot-5

Support SSE-enabled Range functions under Clang

Summary: [Folly] Support SSE-enabled `Range` functions under Clang.

Basically, we remove GCC-only -isms. In particular, we use only names from the `<emmintrins.h>` and `<smmintrins.h>` headers, instead of the GCC-specific SSE builtins that the original code uses.

But in order to get this to work, we must split out the SSE-enabled functions into their own sources and compile them with the `-msse4.2` flag. There is a way to get around this in GCC by marking individual functions as SEE-enabled, but that technique is not supported in Clang. This is the new `RangeSse42` module.

And in order to do that, we must split out the code that is called by both the original `Range` and the new SSE-enabled functions into yet a third module, `RangeCommon`.

Ahhhhhh, dependencies.

Note that we always compile the SSE-enabled functions, but we only run them on architectures supporting SSE. The original code tests the CPU's capabilities before running any SSE-enabled functions; this change retains that check.

Reviewed By: @nbronson

Differential Revision: D2428983
parent b05969e4
...@@ -247,6 +247,8 @@ nobase_follyinclude_HEADERS = \ ...@@ -247,6 +247,8 @@ nobase_follyinclude_HEADERS = \
Random.h \ Random.h \
Random-inl.h \ Random-inl.h \
Range.h \ Range.h \
RangeCommon.h \
RangeSse42.h \
ReadMostlySharedPtr.h \ ReadMostlySharedPtr.h \
RWSpinLock.h \ RWSpinLock.h \
ScopeGuard.h \ ScopeGuard.h \
...@@ -308,6 +310,8 @@ libfollybase_la_SOURCES = \ ...@@ -308,6 +310,8 @@ libfollybase_la_SOURCES = \
FormatTables.cpp \ FormatTables.cpp \
Malloc.cpp \ Malloc.cpp \
Range.cpp \ Range.cpp \
RangeCommon.cpp \
RangeSse42.cpp \
StringBase.cpp \ StringBase.cpp \
String.cpp \ String.cpp \
Unicode.cpp Unicode.cpp
......
...@@ -48,6 +48,8 @@ ...@@ -48,6 +48,8 @@
#include <folly/CpuId.h> #include <folly/CpuId.h>
#include <folly/Traits.h> #include <folly/Traits.h>
#include <folly/Likely.h> #include <folly/Likely.h>
#include <folly/detail/RangeCommon.h>
#include <folly/detail/RangeSse42.h>
// Ignore shadowing warnings within this file, so includers can use -Wshadow. // Ignore shadowing warnings within this file, so includers can use -Wshadow.
#pragma GCC diagnostic push #pragma GCC diagnostic push
...@@ -1000,13 +1002,6 @@ size_t qfind(const Range<T>& haystack, ...@@ -1000,13 +1002,6 @@ size_t qfind(const Range<T>& haystack,
namespace detail { namespace detail {
size_t qfind_first_byte_of_nosse(const StringPiece haystack,
const StringPiece needles);
#if FOLLY_HAVE_EMMINTRIN_H && __GNUC_PREREQ(4, 6)
size_t qfind_first_byte_of_sse42(const StringPiece haystack,
const StringPiece needles);
inline size_t qfind_first_byte_of(const StringPiece haystack, inline size_t qfind_first_byte_of(const StringPiece haystack,
const StringPiece needles) { const StringPiece needles) {
static auto const qfind_first_byte_of_fn = static auto const qfind_first_byte_of_fn =
...@@ -1015,13 +1010,6 @@ inline size_t qfind_first_byte_of(const StringPiece haystack, ...@@ -1015,13 +1010,6 @@ inline size_t qfind_first_byte_of(const StringPiece haystack,
return qfind_first_byte_of_fn(haystack, needles); return qfind_first_byte_of_fn(haystack, needles);
} }
#else
inline size_t qfind_first_byte_of(const StringPiece haystack,
const StringPiece needles) {
return qfind_first_byte_of_nosse(haystack, needles);
}
#endif // FOLLY_HAVE_EMMINTRIN_H
} // namespace detail } // namespace detail
template <class T, class Comp> template <class T, class Comp>
......
...@@ -120,7 +120,7 @@ AM_PATH_PYTHON ...@@ -120,7 +120,7 @@ AM_PATH_PYTHON
# Checks for header files. # Checks for header files.
AC_HEADER_STDC AC_HEADER_STDC
AC_CHECK_HEADERS([fcntl.h features.h inttypes.h limits.h stdint.h stdlib.h string.h sys/time.h unistd.h mutex.h malloc.h emmintrin.h byteswap.h bits/functexcept.h bits/c++config.h]) AC_CHECK_HEADERS([fcntl.h features.h inttypes.h limits.h stdint.h stdlib.h string.h sys/time.h unistd.h mutex.h malloc.h byteswap.h bits/functexcept.h bits/c++config.h])
AC_CHECK_HEADER(double-conversion/double-conversion.h, [], [AC_MSG_ERROR( AC_CHECK_HEADER(double-conversion/double-conversion.h, [], [AC_MSG_ERROR(
[Couldn't find double-conversion.h, please download from \ [Couldn't find double-conversion.h, please download from \
......
/*
* Copyright 2015 Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <folly/detail/RangeCommon.h>
#include <bitset>
#include <folly/SparseByteSet.h>
namespace folly {
namespace detail {
size_t qfind_first_byte_of_bitset(const StringPieceLite haystack,
const StringPieceLite needles) {
std::bitset<256> s;
for (auto needle : needles) {
s[(uint8_t)needle] = true;
}
for (size_t index = 0; index < haystack.size(); ++index) {
if (s[(uint8_t)haystack[index]]) {
return index;
}
}
return std::string::npos;
}
size_t qfind_first_byte_of_byteset(const StringPieceLite haystack,
const StringPieceLite needles) {
SparseByteSet s;
for (auto needle: needles) {
s.add(needle);
}
for (size_t index = 0; index < haystack.size(); ++index) {
if (s.contains(haystack[index])) {
return index;
}
}
return std::string::npos;
}
}
}
/*
* Copyright 2015 Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef FOLLY_DETAIL_RANGE_COMMON_H_
#define FOLLY_DETAIL_RANGE_COMMON_H_
#include <algorithm>
#include <string>
#include <glog/logging.h>
#include <folly/Likely.h>
namespace folly {
namespace detail {
/***
* The qfind_first_byte_of_* functions are declared here, before Range.h, so
* they cannot take StringPiece values. But they're there to operate on
* StringPiece values. Dependency cycles: fun.
*
* StringPieceLite is here to break that dependency cycle.
*/
class StringPieceLite {
public:
StringPieceLite(const char* b, const char* e) : b_(b), e_(e) {}
template <typename Range>
/* implicit */ StringPieceLite(const Range& r) :
StringPieceLite(r.data(), r.data() + r.size()) {}
const char* data() const { return b_; }
const char* begin() const { return b_; }
const char* end() const { return e_; }
size_t size() const { return e_ - b_; }
bool empty() const { return size() == 0; }
const char& operator[](size_t i) const { DCHECK_GT(size(), i); return b_[i]; }
template <typename Range>
explicit operator Range() const { return Range(begin(), end()); }
private:
const char* b_;
const char* e_;
};
inline size_t qfind_first_byte_of_std(const StringPieceLite haystack,
const StringPieceLite needles) {
auto ret = std::find_first_of(haystack.begin(), haystack.end(),
needles.begin(), needles.end(),
[](char a, char b) { return a == b; });
return ret == haystack.end() ? std::string::npos : ret - haystack.begin();
}
size_t qfind_first_byte_of_bitset(const StringPieceLite haystack,
const StringPieceLite needles);
size_t qfind_first_byte_of_byteset(const StringPieceLite haystack,
const StringPieceLite needles);
inline size_t qfind_first_byte_of_nosse(const StringPieceLite haystack,
const StringPieceLite needles) {
if (UNLIKELY(needles.empty() || haystack.empty())) {
return std::string::npos;
}
// The thresholds below were empirically determined by benchmarking.
// This is not an exact science since it depends on the CPU, the size of
// needles, and the size of haystack.
if ((needles.size() >= 4 && haystack.size() <= 10) ||
(needles.size() >= 16 && haystack.size() <= 64) ||
needles.size() >= 32) {
return qfind_first_byte_of_byteset(haystack, needles);
}
return qfind_first_byte_of_std(haystack, needles);
}
}
}
#endif
/*
* Copyright 2015 Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef FOLLY_DETAIL_RANGE_SSE42_H_
#define FOLLY_DETAIL_RANGE_SSE42_H_
#include <cstddef>
#include <folly/detail/RangeCommon.h>
namespace folly {
namespace detail {
size_t qfind_first_byte_of_sse42(const StringPieceLite haystack,
const StringPieceLite needles);
}
}
#endif
...@@ -22,18 +22,6 @@ ...@@ -22,18 +22,6 @@
#include <random> #include <random>
#include <string> #include <string>
namespace folly { namespace detail {
// declaration of functions in Range.cpp
size_t qfind_first_byte_of_byteset(const StringPiece haystack,
const StringPiece needles);
size_t qfind_first_byte_of_bitset(const StringPiece haystack,
const StringPiece needles);
size_t qfind_first_byte_of_nosse(const StringPiece haystack,
const StringPiece needles);
}}
using namespace folly; using namespace folly;
using namespace std; using namespace std;
...@@ -147,12 +135,6 @@ BENCHMARK_RELATIVE(FindSingleCharRange, n) { ...@@ -147,12 +135,6 @@ BENCHMARK_RELATIVE(FindSingleCharRange, n) {
BENCHMARK_DRAW_LINE(); BENCHMARK_DRAW_LINE();
// it's useful to compare our custom implementations vs. the standard library
inline size_t qfind_first_byte_of_std(const StringPiece haystack,
const StringPiece needles) {
return qfind_first_of(haystack, needles, AsciiCaseSensitive());
}
template <class Func> template <class Func>
void countHits(Func func, size_t n) { void countHits(Func func, size_t n) {
StringPiece needles = "\r\n\1"; StringPiece needles = "\r\n\1";
...@@ -188,7 +170,7 @@ BENCHMARK_RELATIVE(FindFirstOf1NeedlesNoSSE, n) { ...@@ -188,7 +170,7 @@ BENCHMARK_RELATIVE(FindFirstOf1NeedlesNoSSE, n) {
} }
BENCHMARK_RELATIVE(FindFirstOf1NeedlesStd, n) { BENCHMARK_RELATIVE(FindFirstOf1NeedlesStd, n) {
findFirstOfRange(delims1, qfind_first_byte_of_std, n); findFirstOfRange(delims1, detail::qfind_first_byte_of_std, n);
} }
BENCHMARK_RELATIVE(FindFirstOf1NeedlesByteSet, n) { BENCHMARK_RELATIVE(FindFirstOf1NeedlesByteSet, n) {
...@@ -212,7 +194,7 @@ BENCHMARK_RELATIVE(FindFirstOf2NeedlesNoSSE, n) { ...@@ -212,7 +194,7 @@ BENCHMARK_RELATIVE(FindFirstOf2NeedlesNoSSE, n) {
} }
BENCHMARK_RELATIVE(FindFirstOf2NeedlesStd, n) { BENCHMARK_RELATIVE(FindFirstOf2NeedlesStd, n) {
findFirstOfRange(delims2, qfind_first_byte_of_std, n); findFirstOfRange(delims2, detail::qfind_first_byte_of_std, n);
} }
BENCHMARK_RELATIVE(FindFirstOf2NeedlesByteSet, n) { BENCHMARK_RELATIVE(FindFirstOf2NeedlesByteSet, n) {
...@@ -236,7 +218,7 @@ BENCHMARK_RELATIVE(FindFirstOf4NeedlesNoSSE, n) { ...@@ -236,7 +218,7 @@ BENCHMARK_RELATIVE(FindFirstOf4NeedlesNoSSE, n) {
} }
BENCHMARK_RELATIVE(FindFirstOf4NeedlesStd, n) { BENCHMARK_RELATIVE(FindFirstOf4NeedlesStd, n) {
findFirstOfRange(delims4, qfind_first_byte_of_std, n); findFirstOfRange(delims4, detail::qfind_first_byte_of_std, n);
} }
BENCHMARK_RELATIVE(FindFirstOf4NeedlesByteSet, n) { BENCHMARK_RELATIVE(FindFirstOf4NeedlesByteSet, n) {
...@@ -260,7 +242,7 @@ BENCHMARK_RELATIVE(FindFirstOf8NeedlesNoSSE, n) { ...@@ -260,7 +242,7 @@ BENCHMARK_RELATIVE(FindFirstOf8NeedlesNoSSE, n) {
} }
BENCHMARK_RELATIVE(FindFirstOf8NeedlesStd, n) { BENCHMARK_RELATIVE(FindFirstOf8NeedlesStd, n) {
findFirstOfRange(delims8, qfind_first_byte_of_std, n); findFirstOfRange(delims8, detail::qfind_first_byte_of_std, n);
} }
BENCHMARK_RELATIVE(FindFirstOf8NeedlesByteSet, n) { BENCHMARK_RELATIVE(FindFirstOf8NeedlesByteSet, n) {
...@@ -284,7 +266,7 @@ BENCHMARK_RELATIVE(FindFirstOf16NeedlesNoSSE, n) { ...@@ -284,7 +266,7 @@ BENCHMARK_RELATIVE(FindFirstOf16NeedlesNoSSE, n) {
} }
BENCHMARK_RELATIVE(FindFirstOf16NeedlesStd, n) { BENCHMARK_RELATIVE(FindFirstOf16NeedlesStd, n) {
findFirstOfRange(delims16, qfind_first_byte_of_std, n); findFirstOfRange(delims16, detail::qfind_first_byte_of_std, n);
} }
BENCHMARK_RELATIVE(FindFirstOf16NeedlesByteSet, n) { BENCHMARK_RELATIVE(FindFirstOf16NeedlesByteSet, n) {
...@@ -308,7 +290,7 @@ BENCHMARK_RELATIVE(FindFirstOf32NeedlesNoSSE, n) { ...@@ -308,7 +290,7 @@ BENCHMARK_RELATIVE(FindFirstOf32NeedlesNoSSE, n) {
} }
BENCHMARK_RELATIVE(FindFirstOf32NeedlesStd, n) { BENCHMARK_RELATIVE(FindFirstOf32NeedlesStd, n) {
findFirstOfRange(delims32, qfind_first_byte_of_std, n); findFirstOfRange(delims32, detail::qfind_first_byte_of_std, n);
} }
BENCHMARK_RELATIVE(FindFirstOf32NeedlesByteSet, n) { BENCHMARK_RELATIVE(FindFirstOf32NeedlesByteSet, n) {
...@@ -333,7 +315,7 @@ BENCHMARK_RELATIVE(FindFirstOf64NeedlesNoSSE, n) { ...@@ -333,7 +315,7 @@ BENCHMARK_RELATIVE(FindFirstOf64NeedlesNoSSE, n) {
} }
BENCHMARK_RELATIVE(FindFirstOf64NeedlesStd, n) { BENCHMARK_RELATIVE(FindFirstOf64NeedlesStd, n) {
findFirstOfRange(delims64, qfind_first_byte_of_std, n); findFirstOfRange(delims64, detail::qfind_first_byte_of_std, n);
} }
BENCHMARK_RELATIVE(FindFirstOf64NeedlesByteSet, n) { BENCHMARK_RELATIVE(FindFirstOf64NeedlesByteSet, n) {
...@@ -364,7 +346,7 @@ BENCHMARK_RELATIVE(FindFirstOfRandomNoSSE, n) { ...@@ -364,7 +346,7 @@ BENCHMARK_RELATIVE(FindFirstOfRandomNoSSE, n) {
} }
BENCHMARK_RELATIVE(FindFirstOfRandomStd, n) { BENCHMARK_RELATIVE(FindFirstOfRandomStd, n) {
findFirstOfRandom(qfind_first_byte_of_std, n); findFirstOfRandom(detail::qfind_first_byte_of_std, n);
} }
BENCHMARK_RELATIVE(FindFirstOfRandomByteSet, n) { BENCHMARK_RELATIVE(FindFirstOfRandomByteSet, n) {
...@@ -386,7 +368,7 @@ BENCHMARK_RELATIVE(CountDelimsNoSSE, n) { ...@@ -386,7 +368,7 @@ BENCHMARK_RELATIVE(CountDelimsNoSSE, n) {
} }
BENCHMARK_RELATIVE(CountDelimsStd, n) { BENCHMARK_RELATIVE(CountDelimsStd, n) {
countHits(qfind_first_byte_of_std, n); countHits(detail::qfind_first_byte_of_std, n);
} }
BENCHMARK_RELATIVE(CountDelimsByteSet, n) { BENCHMARK_RELATIVE(CountDelimsByteSet, n) {
......
...@@ -31,14 +31,6 @@ ...@@ -31,14 +31,6 @@
#include <boost/range/concepts.hpp> #include <boost/range/concepts.hpp>
#include <gtest/gtest.h> #include <gtest/gtest.h>
namespace folly { namespace detail {
// declaration of functions in Range.cpp
size_t qfind_first_byte_of_byteset(const StringPiece haystack,
const StringPiece needles);
}} // namespaces
using namespace folly; using namespace folly;
using namespace std; using namespace std;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment