Skip to content

Commit

Permalink
[共通] levenshtein-sse.hpp 非依存の String::levenshteinDistanceFrom() を非 SS…
Browse files Browse the repository at this point in the history
…E 向けに追加 #1239
  • Loading branch information
Reputeless committed Jun 29, 2024
1 parent b27886b commit 77a07fb
Show file tree
Hide file tree
Showing 2 changed files with 86 additions and 38 deletions.
92 changes: 76 additions & 16 deletions Siv3D/src/Siv3D/String/Levenshtein.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,28 +11,88 @@

# include <Siv3D/Common.hpp>
# include <Siv3D/String.hpp>
# include <Siv3D/Array.hpp>
# include <Siv3D/SIMD.hpp>

# if defined(__GNUC__)
_Pragma("GCC diagnostic push")
_Pragma("GCC diagnostic ignored \"-Wunused-parameter\"")
# if SIV3D_INTRINSIC(SSE)

# if defined(_M_X64)
# define __SSE__ 1
# define __SSE2__ 1
# define __SSE3__ 1
# define __SSSE3__ 1
# define __SSE4_1__ 1
# define __SSE4_2__ 1
# endif
# if defined(__clang__)
_Pragma("clang diagnostic push")
_Pragma("clang diagnostic ignored \"-Wunused-parameter\"")
_Pragma("clang diagnostic ignored \"-Wshorten-64-to-32\"")

# include <ThirdParty/levenshtein-sse/levenshtein-sse.hpp>
# endif
SIV3D_DISABLE_MSVC_WARNINGS_PUSH(4100)
SIV3D_DISABLE_MSVC_WARNINGS_PUSH(4267)
# include <ThirdParty/levenshtein-sse/levenshtein-sse.hpp>
SIV3D_DISABLE_MSVC_WARNINGS_POP()
SIV3D_DISABLE_MSVC_WARNINGS_POP()
SIV3D_DISABLE_CLANG_WARNINGS_POP()
SIV3D_DISABLE_GCC_WARNINGS_POP()

namespace s3d
{
size_t String::levenshteinDistanceFrom(const StringView other) const noexcept
namespace
{
return levenshteinSSE::levenshtein(begin(), end(), other.begin(), other.end());
[[nodiscard]]
static size_t LevenshteinDistance(const StringView s1, const StringView s2) noexcept
{
const size_t minSize = s1.size();
const size_t maxSize = s2.size();

if (maxSize < minSize)
{
return LevenshteinDistance(s2, s1);
}

Array<size_t> distances(minSize + 1);

for (size_t i = 0; i <= minSize; ++i)
{
distances[i] = i;
}

for (size_t k = 1; k <= maxSize; ++k)
{
size_t t = distances[0];

++distances[0];

for (size_t i = 1; i <= minSize; ++i)
{
const size_t old = distances[i];

if (s1[i - 1] == s2[k - 1])
{
distances[i] = t;
}
else
{
distances[i] = (Min(Min(distances[i - 1], distances[i]), t) + 1);
}

t = old;
}
}

return distances[minSize];
}
}

////////////////////////////////////////////////////////////////
//
// levenshteinDistanceFrom
//
////////////////////////////////////////////////////////////////

String::size_type String::levenshteinDistanceFrom(const StringView other) const noexcept
{
# if SIV3D_INTRINSIC(SSE)

return levenshteinSSE::levenshtein(m_string, other);

# else

return LevenshteinDistance(m_string, other);

# endif
}
}
32 changes: 10 additions & 22 deletions Siv3D/src/ThirdParty/levenshtein-sse/levenshtein-sse.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,21 +12,7 @@
#include <iterator>
#include <cstdint>
#include <cassert>

# include <Siv3D/SIMD.hpp>
# ifndef __SSE2__
# define __SSE2__ 1
# endif
# ifndef __SSSE3__
# define __SSSE3__ 1
# endif
# ifndef __SSE4_1__
# define __SSE4_1__ 1
# endif

#ifdef __AVX2__
#include <immintrin.h>
#endif
#include <limits>

namespace levenshteinSSE {

Expand Down Expand Up @@ -198,9 +184,9 @@ constexpr std::size_t alignment = 1;
template<typename Vec1, typename Vec2, typename Iterator1, typename Iterator2>
struct LevenshteinIterationBase {
static inline void perform(const Iterator1& a, const Iterator2& b,
std::size_t& i, std::size_t j, std::size_t bLen, Vec1& diag, const Vec2& diag2)
std::size_t& i, std::size_t j, [[maybe_unused]] std::size_t bLen, Vec1& diag, const Vec2& diag2)
{
std::size_t min = std::min(diag2[i], diag2[i-1]);
std::uint32_t min = static_cast<std::uint32_t>(std::min(diag2[i], diag2[i-1]));
if (min < diag[i-1]) {
diag[i] = min + 1;
}
Expand Down Expand Up @@ -280,7 +266,7 @@ static inline void performSIMD(const T* a, const T* b,

#ifdef __SSSE3__
static inline void performSSE(const T* a, const T* b,
std::size_t& i, std::size_t j, std::size_t bLen,
std::size_t& i, std::size_t j, [[maybe_unused]] std::size_t bLen,
std::uint32_t* diag, const std::uint32_t* diag2)
{
const __m128i one128_epi32 = _mm_set1_epi32(1);
Expand Down Expand Up @@ -315,10 +301,10 @@ static inline void performSSE(const T* a, const T* b,
// We support 1, 2, and 4 byte objects for SSE comparison.
// We always process 16 entries at once, so we may need multiple fetches
// depending on object size.
if (sizeof(T) <= 2) {
if constexpr (sizeof(T) <= 2) {
__m128i substitutionCost16LX, substitutionCost16HX;

if (sizeof(T) == 1) {
if constexpr (sizeof(T) == 1) {
__m128i a_ = _mm_loadu_si128(reinterpret_cast<const __m128i*>(&a[i-16]));
__m128i b_ = _mm_loadu_si128(reinterpret_cast<const __m128i*>(&b[j-1]));
a_ = _mm_shuffle_epi8(a_, reversedIdentity128_epi8);
Expand Down Expand Up @@ -782,10 +768,10 @@ T levenshteinDiagonal(Iterator1 a, Iterator1 aEnd, Iterator2 b, Iterator2 bEnd)
::perform(a, b, i, j, bLen, diag, diag2);
}

diag[0] = k;
diag[0] = static_cast<T>(k);

if (k <= aLen) {
diag[k] = k;
diag[k] = static_cast<T>(k);
}

if (k == aLen + bLen) {
Expand All @@ -796,6 +782,8 @@ T levenshteinDiagonal(Iterator1 a, Iterator1 aEnd, Iterator2 b, Iterator2 bEnd)
// switch buffers
std::swap(diag, diag2);
}

assert(0);
}

/**
Expand Down

0 comments on commit 77a07fb

Please sign in to comment.