#ifndef STRINGZILLAS_SIMILARITIES_HASWELL_HPP_
#define STRINGZILLAS_SIMILARITIES_HASWELL_HPP_
#include "stringzillas/similarities/serial.hpp"
#include "stringzilla/find/haswell.h"
namespace ashvardanian {
namespace stringzillas {
#pragma region Haswell Implementation
#if SZ_USE_HASWELL
#if defined(__clang__)
#pragma clang attribute push(__attribute__((target("avx2,fma,bmi,bmi2"))), apply_to = function)
#elif defined(__GNUC__)
#pragma GCC push_options
#pragma GCC target("avx2", "fma", "bmi", "bmi2")
#endif
struct class_lookup_haswell_t {
u256_vec_t byte_to_class_group_vecs_[16]; u256_vec_t row_subs_low_vec_, row_subs_high_vec_;
u256_vec_t low_nibble_mask_vec_;
class_lookup_haswell_t() noexcept { low_nibble_mask_vec_.ymm = _mm256_set1_epi8(0x0f); }
void reload_classes(u8_t const *byte_to_class) noexcept {
for (int group = 0; group != 16; ++group) {
__m128i group_xmm = _mm_loadu_si128((__m128i const *)(byte_to_class + group * 16));
byte_to_class_group_vecs_[group].ymm = _mm256_set_m128i(group_xmm, group_xmm);
}
}
void reload_row(error_cost_t const *row_subs) noexcept {
__m128i low_xmm = _mm_loadu_si128((__m128i const *)(row_subs + 0));
__m128i high_xmm = _mm_loadu_si128((__m128i const *)(row_subs + 16));
row_subs_low_vec_.ymm = _mm256_set_m128i(low_xmm, low_xmm);
row_subs_high_vec_.ymm = _mm256_set_m128i(high_xmm, high_xmm);
}
SZ_INLINE u256_vec_t classify32(u256_vec_t const &text_vec) const noexcept {
u256_vec_t low_nibbles_vec, high_nibbles_vec, class_vec;
low_nibbles_vec.ymm = _mm256_and_si256(text_vec.ymm, low_nibble_mask_vec_.ymm);
high_nibbles_vec.ymm = _mm256_and_si256(_mm256_srli_epi16(text_vec.ymm, 4), low_nibble_mask_vec_.ymm);
class_vec.ymm = _mm256_setzero_si256();
for (int group = 0; group != 16; ++group) {
__m256i shuffled = _mm256_shuffle_epi8(byte_to_class_group_vecs_[group].ymm, low_nibbles_vec.ymm);
__m256i is_group = _mm256_cmpeq_epi8(high_nibbles_vec.ymm, _mm256_set1_epi8((char)group));
class_vec.ymm = _mm256_blendv_epi8(class_vec.ymm, shuffled, is_group);
}
return class_vec;
}
SZ_INLINE u256_vec_t costs_for_classes32(u256_vec_t const &class_vec) const noexcept {
u256_vec_t substituted_vec;
__m256i cost_if_low = _mm256_shuffle_epi8(row_subs_low_vec_.ymm, class_vec.ymm);
__m256i cost_if_high = _mm256_shuffle_epi8(row_subs_high_vec_.ymm, class_vec.ymm);
__m256i is_high_class = _mm256_cmpgt_epi8(class_vec.ymm, _mm256_set1_epi8(15));
substituted_vec.ymm = _mm256_blendv_epi8(cost_if_low, cost_if_high, is_high_class);
return substituted_vec;
}
SZ_INLINE u256_vec_t lookup32(u256_vec_t const &text_vec) const noexcept {
return costs_for_classes32(classify32(text_vec));
}
};
struct substitution_lookup_haswell_t {
u256_vec_t byte_to_class_group_vecs_[16]; u256_vec_t cost_rows_low_vecs_[error_costs_classes_count_k], cost_rows_high_vecs_[error_costs_classes_count_k];
u256_vec_t low_nibble_mask_vec_;
substitution_lookup_haswell_t() noexcept { low_nibble_mask_vec_.ymm = _mm256_set1_epi8(0x0f); }
void reload_classes(u8_t const *byte_to_class) noexcept {
for (int group = 0; group != 16; ++group) {
__m128i group_xmm = _mm_loadu_si128((__m128i const *)(byte_to_class + group * 16));
byte_to_class_group_vecs_[group].ymm = _mm256_set_m128i(group_xmm, group_xmm);
}
}
void reload_costs(
error_cost_t const (&class_substitution_costs)[error_costs_classes_count_k][error_costs_classes_count_k],
bool transpose) noexcept {
for (size_t first_class = 0; first_class != error_costs_classes_count_k; ++first_class) {
error_cost_t row[error_costs_classes_count_k];
for (size_t second_class = 0; second_class != error_costs_classes_count_k; ++second_class)
row[second_class] = transpose ? class_substitution_costs[second_class][first_class]
: class_substitution_costs[first_class][second_class];
__m128i low_xmm = _mm_loadu_si128((__m128i const *)(row + 0));
__m128i high_xmm = _mm_loadu_si128((__m128i const *)(row + 16));
cost_rows_low_vecs_[first_class].ymm = _mm256_set_m128i(low_xmm, low_xmm);
cost_rows_high_vecs_[first_class].ymm = _mm256_set_m128i(high_xmm, high_xmm);
}
}
SZ_INLINE u256_vec_t classify32(u256_vec_t const &text_vec) const noexcept {
u256_vec_t low_nibbles_vec, high_nibbles_vec, class_vec;
low_nibbles_vec.ymm = _mm256_and_si256(text_vec.ymm, low_nibble_mask_vec_.ymm);
high_nibbles_vec.ymm = _mm256_and_si256(_mm256_srli_epi16(text_vec.ymm, 4), low_nibble_mask_vec_.ymm);
class_vec.ymm = _mm256_setzero_si256();
for (int group = 0; group != 16; ++group) {
__m256i shuffled = _mm256_shuffle_epi8(byte_to_class_group_vecs_[group].ymm, low_nibbles_vec.ymm);
__m256i is_group = _mm256_cmpeq_epi8(high_nibbles_vec.ymm, _mm256_set1_epi8((char)group));
class_vec.ymm = _mm256_blendv_epi8(class_vec.ymm, shuffled, is_group);
}
return class_vec;
}
SZ_INLINE u256_vec_t lookup32(u256_vec_t const &first_class_vec,
u256_vec_t const &second_class_vec) const noexcept {
u256_vec_t cost_low_vecs[error_costs_classes_count_k], cost_high_vecs[error_costs_classes_count_k];
u256_vec_t substituted_vec;
__m256i const second_idx = second_class_vec.ymm;
for (size_t row = 0; row != error_costs_classes_count_k; ++row) {
cost_low_vecs[row].ymm = _mm256_shuffle_epi8(cost_rows_low_vecs_[row].ymm, second_idx);
cost_high_vecs[row].ymm = _mm256_shuffle_epi8(cost_rows_high_vecs_[row].ymm, second_idx);
}
for (int bit = 0; bit != 5; ++bit) {
size_t const survivors = (size_t)error_costs_classes_count_k >> (bit + 1);
__m256i const bit_value = _mm256_set1_epi8((char)(1 << bit));
__m256i const is_bit_set = _mm256_cmpeq_epi8(_mm256_and_si256(first_class_vec.ymm, bit_value), bit_value);
for (size_t pair = 0; pair != survivors; ++pair) {
cost_low_vecs[pair].ymm = _mm256_blendv_epi8(cost_low_vecs[2 * pair].ymm,
cost_low_vecs[2 * pair + 1].ymm, is_bit_set);
cost_high_vecs[pair].ymm = _mm256_blendv_epi8(cost_high_vecs[2 * pair].ymm,
cost_high_vecs[2 * pair + 1].ymm, is_bit_set);
}
}
__m256i const is_high_class = _mm256_cmpgt_epi8(second_class_vec.ymm, _mm256_set1_epi8(15));
substituted_vec.ymm = _mm256_blendv_epi8(cost_low_vecs[0].ymm, cost_high_vecs[0].ymm, is_high_class);
return substituted_vec;
}
};
template <sz_similarity_locality_t locality_>
struct tile_scorer<constant_iterator<char>, char const *, i16_t, error_costs_32x32_t, linear_gap_costs_t,
sz_maximize_score_k, locality_, sz_cap_haswell_k>
: public tile_scorer<constant_iterator<char>, char const *, i16_t, error_costs_32x32_t, linear_gap_costs_t,
sz_maximize_score_k, locality_, sz_cap_serial_k, void> {
using tile_scorer<constant_iterator<char>, char const *, i16_t, error_costs_32x32_t, linear_gap_costs_t,
sz_maximize_score_k, locality_, sz_cap_serial_k,
void>::tile_scorer;
static constexpr sz_similarity_objective_t objective_k = sz_maximize_score_k;
static constexpr sz_similarity_locality_t locality_k = locality_;
static constexpr sz_capability_t capability_k = sz_cap_haswell_k;
class_lookup_haswell_t lookup_;
SZ_NOINLINE void score_slice_trampoline_( char const *second_slice, i16_t gap, i16_t const *scores_pre_substitution, i16_t const *scores_pre_insertion, i16_t *scores_new, size_t from, size_t to) const noexcept {
for (size_t idx_slice = from; idx_slice < to; ++idx_slice)
slice_32chars(second_slice, idx_slice * 32, gap, scores_pre_substitution, scores_pre_insertion, scores_new);
}
template <typename executor_type_ = dummy_executor_t>
#if SZ_HAS_CONCEPTS_
requires executor_like<executor_type_>
#endif
void operator()( constant_iterator<char> first_char, char const *second_slice, size_t n, i16_t const *scores_pre_substitution, i16_t const *scores_pre_insertion, i16_t const *scores_pre_deletion, i16_t *scores_new, executor_type_ &&executor = {}) noexcept {
i16_t const gap = static_cast<i16_t>(this->gap_costs_.open_or_extend);
u8_t const first_class = this->substituter_.byte_to_class[(u8_t)*first_char];
lookup_.reload_classes(this->substituter_.byte_to_class);
lookup_.reload_row(&this->substituter_.class_substitution_costs[first_class][0]);
size_t const count_slices = n / 32;
executor.for_slices(count_slices, [&](size_t from, size_t to) noexcept {
score_slice_trampoline_(second_slice, gap, scores_pre_substitution, scores_pre_insertion, scores_new, from,
to);
});
for (size_t i = count_slices * 32; i < n; ++i)
slice_1char(second_slice, i, gap, scores_pre_substitution, scores_pre_insertion, scores_new);
sz_assert_(scores_pre_substitution + 1 == scores_pre_insertion && "Expects horizontal traversal of DP matrix");
sz_assert_(scores_pre_deletion + 1 == scores_new && "Expects horizontal traversal of DP matrix");
i16_t last_in_row = scores_pre_deletion[0];
if constexpr (locality_ == sz_similarity_global_k) {
for (size_t i = 0; i < n; ++i)
scores_new[i] = last_in_row = sz_max_of_two(scores_new[i], last_in_row + gap);
this->last_score_ = last_in_row;
}
else {
i16_t row_best = this->best_score_;
for (size_t i = 0; i < n; ++i) {
scores_new[i] = last_in_row = sz_max_of_two(scores_new[i], last_in_row + gap);
row_best = sz_max_of_two(row_best, scores_new[i]);
}
this->best_score_ = row_best;
}
}
void slice_32chars(char const *second_slice, size_t i, i16_t gap, i16_t const *scores_pre_substitution, i16_t const *scores_pre_insertion, i16_t *scores_new) const noexcept {
u256_vec_t second_vec;
u256_vec_t pre_substitution_vecs[2], pre_gap_vecs[2];
u256_vec_t cost_of_substitution_i8_vec, cost_of_substitution_i16_vecs[2];
u256_vec_t cost_if_substitution_vecs[2], cost_if_gap_vecs[2], cell_score_vecs[2];
u256_vec_t gap_cost_vec;
gap_cost_vec.ymm = _mm256_set1_epi16(gap);
second_vec.ymm = _mm256_loadu_si256((__m256i const *)(second_slice + i));
pre_substitution_vecs[0].ymm = _mm256_loadu_si256((__m256i const *)(scores_pre_substitution + i + 0));
pre_substitution_vecs[1].ymm = _mm256_loadu_si256((__m256i const *)(scores_pre_substitution + i + 16));
pre_gap_vecs[0].ymm = _mm256_loadu_si256((__m256i const *)(scores_pre_insertion + i + 0));
pre_gap_vecs[1].ymm = _mm256_loadu_si256((__m256i const *)(scores_pre_insertion + i + 16));
cost_of_substitution_i8_vec = lookup_.lookup32(second_vec);
cost_of_substitution_i16_vecs[0].ymm = _mm256_cvtepi8_epi16(
_mm256_extracti128_si256(cost_of_substitution_i8_vec.ymm, 0));
cost_of_substitution_i16_vecs[1].ymm = _mm256_cvtepi8_epi16(
_mm256_extracti128_si256(cost_of_substitution_i8_vec.ymm, 1));
cost_if_substitution_vecs[0].ymm = _mm256_add_epi16(pre_substitution_vecs[0].ymm,
cost_of_substitution_i16_vecs[0].ymm);
cost_if_substitution_vecs[1].ymm = _mm256_add_epi16(pre_substitution_vecs[1].ymm,
cost_of_substitution_i16_vecs[1].ymm);
cost_if_gap_vecs[0].ymm = _mm256_add_epi16(pre_gap_vecs[0].ymm, gap_cost_vec.ymm);
cost_if_gap_vecs[1].ymm = _mm256_add_epi16(pre_gap_vecs[1].ymm, gap_cost_vec.ymm);
cell_score_vecs[0].ymm = _mm256_max_epi16(cost_if_substitution_vecs[0].ymm, cost_if_gap_vecs[0].ymm);
cell_score_vecs[1].ymm = _mm256_max_epi16(cost_if_substitution_vecs[1].ymm, cost_if_gap_vecs[1].ymm);
if constexpr (locality_ == sz_similarity_local_k)
cell_score_vecs[0].ymm = _mm256_max_epi16(cell_score_vecs[0].ymm, _mm256_setzero_si256()),
cell_score_vecs[1].ymm = _mm256_max_epi16(cell_score_vecs[1].ymm, _mm256_setzero_si256());
_mm256_storeu_si256((__m256i *)(scores_new + i + 0), cell_score_vecs[0].ymm);
_mm256_storeu_si256((__m256i *)(scores_new + i + 16), cell_score_vecs[1].ymm);
}
void slice_1char(char const *second_slice, size_t i, i16_t gap, i16_t const *scores_pre_substitution, i16_t const *scores_pre_insertion, i16_t *scores_new) const noexcept {
u8_t const second_class = this->substituter_.byte_to_class[(u8_t)second_slice[i]];
i16_t const cost_of_substitution = second_class < 16 ? lookup_.row_subs_low_vec_.i8s[second_class]
: lookup_.row_subs_high_vec_.i8s[second_class - 16];
i16_t const if_substitution = scores_pre_substitution[i] + cost_of_substitution;
i16_t const if_gap = scores_pre_insertion[i] + gap;
i16_t cell_score = sz_max_of_two(if_substitution, if_gap);
if constexpr (locality_ == sz_similarity_local_k) cell_score = sz_max_of_two(cell_score, (i16_t)0);
scores_new[i] = cell_score;
}
};
template <sz_similarity_locality_t locality_>
struct tile_scorer<constant_iterator<char>, char const *, i32_t, error_costs_32x32_t, linear_gap_costs_t,
sz_maximize_score_k, locality_, sz_cap_haswell_k, void>
: public tile_scorer<constant_iterator<char>, char const *, i32_t, error_costs_32x32_t, linear_gap_costs_t,
sz_maximize_score_k, locality_, sz_cap_serial_k, void> {
using tile_scorer<constant_iterator<char>, char const *, i32_t, error_costs_32x32_t, linear_gap_costs_t,
sz_maximize_score_k, locality_, sz_cap_serial_k,
void>::tile_scorer;
static constexpr sz_similarity_objective_t objective_k = sz_maximize_score_k;
static constexpr sz_similarity_locality_t locality_k = locality_;
static constexpr sz_capability_t capability_k = sz_cap_haswell_k;
class_lookup_haswell_t lookup_;
SZ_NOINLINE void score_slice_trampoline_( char const *second_slice, i32_t gap, i32_t const *scores_pre_substitution, i32_t const *scores_pre_insertion, i32_t *scores_new, size_t from, size_t to) const noexcept {
for (size_t idx_slice = from; idx_slice < to; ++idx_slice)
slice_32chars(second_slice, idx_slice * 32, gap, scores_pre_substitution, scores_pre_insertion, scores_new);
}
template <typename executor_type_ = dummy_executor_t>
#if SZ_HAS_CONCEPTS_
requires executor_like<executor_type_>
#endif
void operator()( constant_iterator<char> first_char, char const *second_slice, size_t n, i32_t const *scores_pre_substitution, i32_t const *scores_pre_insertion, i32_t const *scores_pre_deletion, i32_t *scores_new, executor_type_ &&executor = {}) noexcept {
i32_t const gap = static_cast<i32_t>(this->gap_costs_.open_or_extend);
u8_t const first_class = this->substituter_.byte_to_class[(u8_t)*first_char];
lookup_.reload_classes(this->substituter_.byte_to_class);
lookup_.reload_row(&this->substituter_.class_substitution_costs[first_class][0]);
size_t const count_slices = n / 32;
executor.for_slices(count_slices, [&](size_t from, size_t to) noexcept {
score_slice_trampoline_(second_slice, gap, scores_pre_substitution, scores_pre_insertion, scores_new, from,
to);
});
for (size_t i = count_slices * 32; i < n; ++i)
slice_1char(second_slice, i, gap, scores_pre_substitution, scores_pre_insertion, scores_new);
sz_assert_(scores_pre_substitution + 1 == scores_pre_insertion && "Expects horizontal traversal of DP matrix");
sz_assert_(scores_pre_deletion + 1 == scores_new && "Expects horizontal traversal of DP matrix");
i32_t last_in_row = scores_pre_deletion[0];
if constexpr (locality_ == sz_similarity_global_k) {
for (size_t i = 0; i < n; ++i)
scores_new[i] = last_in_row = sz_max_of_two(scores_new[i], last_in_row + gap);
this->last_score_ = last_in_row;
}
else {
i32_t row_best = this->best_score_;
for (size_t i = 0; i < n; ++i) {
scores_new[i] = last_in_row = sz_max_of_two(scores_new[i], last_in_row + gap);
row_best = sz_max_of_two(row_best, scores_new[i]);
}
this->best_score_ = row_best;
}
}
void slice_32chars(char const *second_slice, size_t i, i32_t gap, i32_t const *scores_pre_substitution, i32_t const *scores_pre_insertion, i32_t *scores_new) const noexcept {
u256_vec_t second_vec;
u256_vec_t pre_substitution_vecs[4], pre_gap_vecs[4];
u256_vec_t cost_of_substitution_i8_vec, cost_of_substitution_i32_vecs[4];
u256_vec_t cost_if_substitution_vecs[4], cost_if_gap_vecs[4], cell_score_vecs[4];
u256_vec_t gap_cost_vec;
gap_cost_vec.ymm = _mm256_set1_epi32(gap);
second_vec.ymm = _mm256_loadu_si256((__m256i const *)(second_slice + i));
pre_substitution_vecs[0].ymm = _mm256_loadu_si256((__m256i const *)(scores_pre_substitution + i + 8 * 0));
pre_substitution_vecs[1].ymm = _mm256_loadu_si256((__m256i const *)(scores_pre_substitution + i + 8 * 1));
pre_substitution_vecs[2].ymm = _mm256_loadu_si256((__m256i const *)(scores_pre_substitution + i + 8 * 2));
pre_substitution_vecs[3].ymm = _mm256_loadu_si256((__m256i const *)(scores_pre_substitution + i + 8 * 3));
pre_gap_vecs[0].ymm = _mm256_loadu_si256((__m256i const *)(scores_pre_insertion + i + 8 * 0));
pre_gap_vecs[1].ymm = _mm256_loadu_si256((__m256i const *)(scores_pre_insertion + i + 8 * 1));
pre_gap_vecs[2].ymm = _mm256_loadu_si256((__m256i const *)(scores_pre_insertion + i + 8 * 2));
pre_gap_vecs[3].ymm = _mm256_loadu_si256((__m256i const *)(scores_pre_insertion + i + 8 * 3));
cost_of_substitution_i8_vec = lookup_.lookup32(second_vec);
__m128i cost_low_xmm = _mm256_extracti128_si256(cost_of_substitution_i8_vec.ymm, 0);
__m128i cost_high_xmm = _mm256_extracti128_si256(cost_of_substitution_i8_vec.ymm, 1);
cost_of_substitution_i32_vecs[0].ymm = _mm256_cvtepi8_epi32(cost_low_xmm);
cost_of_substitution_i32_vecs[1].ymm = _mm256_cvtepi8_epi32(_mm_srli_si128(cost_low_xmm, 8));
cost_of_substitution_i32_vecs[2].ymm = _mm256_cvtepi8_epi32(cost_high_xmm);
cost_of_substitution_i32_vecs[3].ymm = _mm256_cvtepi8_epi32(_mm_srli_si128(cost_high_xmm, 8));
cost_if_substitution_vecs[0].ymm = _mm256_add_epi32(pre_substitution_vecs[0].ymm,
cost_of_substitution_i32_vecs[0].ymm);
cost_if_substitution_vecs[1].ymm = _mm256_add_epi32(pre_substitution_vecs[1].ymm,
cost_of_substitution_i32_vecs[1].ymm);
cost_if_substitution_vecs[2].ymm = _mm256_add_epi32(pre_substitution_vecs[2].ymm,
cost_of_substitution_i32_vecs[2].ymm);
cost_if_substitution_vecs[3].ymm = _mm256_add_epi32(pre_substitution_vecs[3].ymm,
cost_of_substitution_i32_vecs[3].ymm);
cost_if_gap_vecs[0].ymm = _mm256_add_epi32(pre_gap_vecs[0].ymm, gap_cost_vec.ymm);
cost_if_gap_vecs[1].ymm = _mm256_add_epi32(pre_gap_vecs[1].ymm, gap_cost_vec.ymm);
cost_if_gap_vecs[2].ymm = _mm256_add_epi32(pre_gap_vecs[2].ymm, gap_cost_vec.ymm);
cost_if_gap_vecs[3].ymm = _mm256_add_epi32(pre_gap_vecs[3].ymm, gap_cost_vec.ymm);
cell_score_vecs[0].ymm = _mm256_max_epi32(cost_if_substitution_vecs[0].ymm, cost_if_gap_vecs[0].ymm);
cell_score_vecs[1].ymm = _mm256_max_epi32(cost_if_substitution_vecs[1].ymm, cost_if_gap_vecs[1].ymm);
cell_score_vecs[2].ymm = _mm256_max_epi32(cost_if_substitution_vecs[2].ymm, cost_if_gap_vecs[2].ymm);
cell_score_vecs[3].ymm = _mm256_max_epi32(cost_if_substitution_vecs[3].ymm, cost_if_gap_vecs[3].ymm);
if constexpr (locality_ == sz_similarity_local_k)
cell_score_vecs[0].ymm = _mm256_max_epi32(cell_score_vecs[0].ymm, _mm256_setzero_si256()),
cell_score_vecs[1].ymm = _mm256_max_epi32(cell_score_vecs[1].ymm, _mm256_setzero_si256()),
cell_score_vecs[2].ymm = _mm256_max_epi32(cell_score_vecs[2].ymm, _mm256_setzero_si256()),
cell_score_vecs[3].ymm = _mm256_max_epi32(cell_score_vecs[3].ymm, _mm256_setzero_si256());
_mm256_storeu_si256((__m256i *)(scores_new + i + 8 * 0), cell_score_vecs[0].ymm);
_mm256_storeu_si256((__m256i *)(scores_new + i + 8 * 1), cell_score_vecs[1].ymm);
_mm256_storeu_si256((__m256i *)(scores_new + i + 8 * 2), cell_score_vecs[2].ymm);
_mm256_storeu_si256((__m256i *)(scores_new + i + 8 * 3), cell_score_vecs[3].ymm);
}
void slice_1char(char const *second_slice, size_t i, i32_t gap, i32_t const *scores_pre_substitution, i32_t const *scores_pre_insertion, i32_t *scores_new) const noexcept {
u8_t const second_class = this->substituter_.byte_to_class[(u8_t)second_slice[i]];
i32_t const cost_of_substitution = second_class < 16 ? lookup_.row_subs_low_vec_.i8s[second_class]
: lookup_.row_subs_high_vec_.i8s[second_class - 16];
i32_t const if_substitution = scores_pre_substitution[i] + cost_of_substitution;
i32_t const if_gap = scores_pre_insertion[i] + gap;
i32_t cell_score = sz_max_of_two(if_substitution, if_gap);
if constexpr (locality_ == sz_similarity_local_k) cell_score = sz_max_of_two(cell_score, (i32_t)0);
scores_new[i] = cell_score;
}
};
template <sz_similarity_locality_t locality_>
struct tile_scorer<constant_iterator<char>, char const *, i64_t, error_costs_32x32_t, linear_gap_costs_t,
sz_maximize_score_k, locality_, sz_cap_haswell_k>
: public tile_scorer<constant_iterator<char>, char const *, i64_t, error_costs_32x32_t, linear_gap_costs_t,
sz_maximize_score_k, locality_, sz_cap_serial_k, void> {
using tile_scorer<constant_iterator<char>, char const *, i64_t, error_costs_32x32_t, linear_gap_costs_t,
sz_maximize_score_k, locality_, sz_cap_serial_k,
void>::tile_scorer; };
template <sz_capability_t capability_>
struct tile_scorer<char const *, char const *, i16_t, error_costs_32x32_t, linear_gap_costs_t, sz_maximize_score_k,
sz_similarity_global_k, capability_, std::enable_if_t<(capability_ & sz_cap_haswell_k) != 0>>
: public tile_scorer<char const *, char const *, i16_t, error_costs_32x32_t, linear_gap_costs_t,
sz_maximize_score_k, sz_similarity_global_k, sz_cap_serial_k, void> {
using tile_scorer<char const *, char const *, i16_t, error_costs_32x32_t, linear_gap_costs_t, sz_maximize_score_k,
sz_similarity_global_k, sz_cap_serial_k, void>::tile_scorer;
static constexpr sz_similarity_objective_t objective_k = sz_maximize_score_k;
static constexpr sz_similarity_locality_t locality_k = sz_similarity_global_k;
static constexpr sz_capability_t capability_k = capability_;
static constexpr size_t step_k = 32;
substitution_lookup_haswell_t lookup_;
void prepare(bool transpose) noexcept {
lookup_.reload_costs(this->substituter_.class_substitution_costs, transpose);
}
SZ_INLINE void slice_32cells( u8_t const *first_reversed_slice, u8_t const *second_slice, i16_t const *scores_pre_substitution, i16_t const *scores_pre_insertion, i16_t const *scores_pre_deletion, i16_t *scores_new, u256_vec_t gap_cost_vec) const noexcept {
u256_vec_t first_vec, second_vec;
u256_vec_t cost_of_substitution_i8_vec, cost_of_substitution_i16_vecs[2];
u256_vec_t pre_substitution_vecs[2], pre_insert_vecs[2], pre_delete_vecs[2];
u256_vec_t cost_if_substitution_vecs[2], cost_if_gap_vecs[2], cell_score_vecs[2];
first_vec.ymm = _mm256_loadu_si256((__m256i const *)first_reversed_slice);
second_vec.ymm = _mm256_loadu_si256((__m256i const *)second_slice);
pre_substitution_vecs[0].ymm = _mm256_loadu_si256((__m256i const *)(scores_pre_substitution + 0));
pre_substitution_vecs[1].ymm = _mm256_loadu_si256((__m256i const *)(scores_pre_substitution + 16));
pre_insert_vecs[0].ymm = _mm256_loadu_si256((__m256i const *)(scores_pre_insertion + 0));
pre_insert_vecs[1].ymm = _mm256_loadu_si256((__m256i const *)(scores_pre_insertion + 16));
pre_delete_vecs[0].ymm = _mm256_loadu_si256((__m256i const *)(scores_pre_deletion + 0));
pre_delete_vecs[1].ymm = _mm256_loadu_si256((__m256i const *)(scores_pre_deletion + 16));
cost_of_substitution_i8_vec = lookup_.lookup32(first_vec, second_vec);
cost_of_substitution_i16_vecs[0].ymm = _mm256_cvtepi8_epi16(
_mm256_extracti128_si256(cost_of_substitution_i8_vec.ymm, 0));
cost_of_substitution_i16_vecs[1].ymm = _mm256_cvtepi8_epi16(
_mm256_extracti128_si256(cost_of_substitution_i8_vec.ymm, 1));
cost_if_substitution_vecs[0].ymm = _mm256_add_epi16(pre_substitution_vecs[0].ymm,
cost_of_substitution_i16_vecs[0].ymm);
cost_if_substitution_vecs[1].ymm = _mm256_add_epi16(pre_substitution_vecs[1].ymm,
cost_of_substitution_i16_vecs[1].ymm);
cost_if_gap_vecs[0].ymm = _mm256_add_epi16(_mm256_max_epi16(pre_insert_vecs[0].ymm, pre_delete_vecs[0].ymm),
gap_cost_vec.ymm);
cost_if_gap_vecs[1].ymm = _mm256_add_epi16(_mm256_max_epi16(pre_insert_vecs[1].ymm, pre_delete_vecs[1].ymm),
gap_cost_vec.ymm);
cell_score_vecs[0].ymm = _mm256_max_epi16(cost_if_substitution_vecs[0].ymm, cost_if_gap_vecs[0].ymm);
cell_score_vecs[1].ymm = _mm256_max_epi16(cost_if_substitution_vecs[1].ymm, cost_if_gap_vecs[1].ymm);
_mm256_storeu_si256((__m256i *)(scores_new + 0), cell_score_vecs[0].ymm);
_mm256_storeu_si256((__m256i *)(scores_new + 16), cell_score_vecs[1].ymm);
}
SZ_INLINE void slice_1cell( u8_t const *first_reversed_slice, u8_t const *second_slice, i16_t const *scores_pre_substitution, i16_t const *scores_pre_insertion, i16_t const *scores_pre_deletion, i16_t *scores_new, i16_t gap) const noexcept {
u8_t const first_class = first_reversed_slice[0], second_class = second_slice[0];
i16_t const sub_cost = second_class < 16 ? lookup_.cost_rows_low_vecs_[first_class].i8s[second_class]
: lookup_.cost_rows_high_vecs_[first_class].i8s[second_class - 16];
i16_t const if_substitution = scores_pre_substitution[0] + sub_cost;
i16_t const if_gap = sz_max_of_two(scores_pre_insertion[0], scores_pre_deletion[0]) + gap;
scores_new[0] = sz_max_of_two(if_substitution, if_gap);
}
SZ_NOINLINE void score_slice_trampoline_( u8_t const *first_reversed_classes, u8_t const *second_classes, i16_t const *scores_pre_substitution, i16_t const *scores_pre_insertion, i16_t const *scores_pre_deletion, i16_t *scores_new, i16_t gap, size_t from, size_t to) const noexcept {
u256_vec_t gap_cost_vec;
gap_cost_vec.ymm = _mm256_set1_epi16(gap);
for (size_t idx_slice = from; idx_slice < to; ++idx_slice) {
size_t const progress = idx_slice * step_k;
slice_32cells( first_reversed_classes + progress, second_classes + progress, scores_pre_substitution + progress, scores_pre_insertion + progress, scores_pre_deletion + progress, scores_new + progress, gap_cost_vec);
}
}
template <typename executor_type_ = dummy_executor_t>
#if SZ_HAS_CONCEPTS_
requires executor_like<executor_type_>
#endif
void operator()( char const *first_reversed_slice, char const *second_slice, size_t const length, i16_t const *scores_pre_substitution, i16_t const *scores_pre_insertion, i16_t const *scores_pre_deletion, i16_t *scores_new, executor_type_ &&executor = {}) noexcept {
u8_t const *first_reversed_classes = (u8_t const *)first_reversed_slice;
u8_t const *second_classes = (u8_t const *)second_slice;
i16_t const gap = static_cast<i16_t>(this->gap_costs_.open_or_extend);
size_t const count_slices = length / step_k;
executor.for_slices(count_slices, [&](size_t from, size_t to) noexcept {
score_slice_trampoline_(first_reversed_classes, second_classes, scores_pre_substitution,
scores_pre_insertion, scores_pre_deletion, scores_new, gap, from, to);
});
for (size_t i = count_slices * step_k; i < length; ++i)
slice_1cell(first_reversed_classes + i, second_classes + i, scores_pre_substitution + i,
scores_pre_insertion + i, scores_pre_deletion + i, scores_new + i, gap);
this->last_score_ = scores_new[length - 1];
}
};
template <sz_capability_t capability_>
struct tile_scorer<char const *, char const *, i16_t, error_costs_32x32_t, linear_gap_costs_t, sz_maximize_score_k,
sz_similarity_local_k, capability_, std::enable_if_t<(capability_ & sz_cap_haswell_k) != 0>>
: public tile_scorer<char const *, char const *, i16_t, error_costs_32x32_t, linear_gap_costs_t,
sz_maximize_score_k, sz_similarity_local_k, sz_cap_serial_k, void> {
using tile_scorer<char const *, char const *, i16_t, error_costs_32x32_t, linear_gap_costs_t, sz_maximize_score_k,
sz_similarity_local_k, sz_cap_serial_k, void>::tile_scorer;
static constexpr sz_similarity_objective_t objective_k = sz_maximize_score_k;
static constexpr sz_similarity_locality_t locality_k = sz_similarity_local_k;
static constexpr sz_capability_t capability_k = capability_;
static constexpr size_t step_k = 32;
substitution_lookup_haswell_t lookup_;
void prepare(bool transpose) noexcept {
lookup_.reload_costs(this->substituter_.class_substitution_costs, transpose);
}
SZ_INLINE void slice_32cells( u8_t const *first_reversed_slice, u8_t const *second_slice, i16_t const *scores_pre_substitution, i16_t const *scores_pre_insertion, i16_t const *scores_pre_deletion, i16_t *scores_new, u256_vec_t gap_cost_vec) const noexcept {
u256_vec_t first_vec, second_vec;
u256_vec_t cost_of_substitution_i8_vec, cost_of_substitution_i16_vecs[2];
u256_vec_t pre_substitution_vecs[2], pre_insert_vecs[2], pre_delete_vecs[2];
u256_vec_t cost_if_substitution_vecs[2], cost_if_gap_vecs[2], cell_score_vecs[2];
first_vec.ymm = _mm256_loadu_si256((__m256i const *)first_reversed_slice);
second_vec.ymm = _mm256_loadu_si256((__m256i const *)second_slice);
pre_substitution_vecs[0].ymm = _mm256_loadu_si256((__m256i const *)(scores_pre_substitution + 0));
pre_substitution_vecs[1].ymm = _mm256_loadu_si256((__m256i const *)(scores_pre_substitution + 16));
pre_insert_vecs[0].ymm = _mm256_loadu_si256((__m256i const *)(scores_pre_insertion + 0));
pre_insert_vecs[1].ymm = _mm256_loadu_si256((__m256i const *)(scores_pre_insertion + 16));
pre_delete_vecs[0].ymm = _mm256_loadu_si256((__m256i const *)(scores_pre_deletion + 0));
pre_delete_vecs[1].ymm = _mm256_loadu_si256((__m256i const *)(scores_pre_deletion + 16));
cost_of_substitution_i8_vec = lookup_.lookup32(first_vec, second_vec);
cost_of_substitution_i16_vecs[0].ymm = _mm256_cvtepi8_epi16(
_mm256_extracti128_si256(cost_of_substitution_i8_vec.ymm, 0));
cost_of_substitution_i16_vecs[1].ymm = _mm256_cvtepi8_epi16(
_mm256_extracti128_si256(cost_of_substitution_i8_vec.ymm, 1));
cost_if_substitution_vecs[0].ymm = _mm256_add_epi16(pre_substitution_vecs[0].ymm,
cost_of_substitution_i16_vecs[0].ymm);
cost_if_substitution_vecs[1].ymm = _mm256_add_epi16(pre_substitution_vecs[1].ymm,
cost_of_substitution_i16_vecs[1].ymm);
cost_if_gap_vecs[0].ymm = _mm256_add_epi16(_mm256_max_epi16(pre_insert_vecs[0].ymm, pre_delete_vecs[0].ymm),
gap_cost_vec.ymm);
cost_if_gap_vecs[1].ymm = _mm256_add_epi16(_mm256_max_epi16(pre_insert_vecs[1].ymm, pre_delete_vecs[1].ymm),
gap_cost_vec.ymm);
cell_score_vecs[0].ymm = _mm256_max_epi16(cost_if_substitution_vecs[0].ymm, cost_if_gap_vecs[0].ymm);
cell_score_vecs[1].ymm = _mm256_max_epi16(cost_if_substitution_vecs[1].ymm, cost_if_gap_vecs[1].ymm);
cell_score_vecs[0].ymm = _mm256_max_epi16(cell_score_vecs[0].ymm, _mm256_setzero_si256());
cell_score_vecs[1].ymm = _mm256_max_epi16(cell_score_vecs[1].ymm, _mm256_setzero_si256());
_mm256_storeu_si256((__m256i *)(scores_new + 0), cell_score_vecs[0].ymm);
_mm256_storeu_si256((__m256i *)(scores_new + 16), cell_score_vecs[1].ymm);
}
SZ_INLINE void slice_1cell( u8_t const *first_reversed_slice, u8_t const *second_slice, i16_t const *scores_pre_substitution, i16_t const *scores_pre_insertion, i16_t const *scores_pre_deletion, i16_t *scores_new, i16_t gap) const noexcept {
u8_t const first_class = first_reversed_slice[0], second_class = second_slice[0];
i16_t const sub_cost = second_class < 16 ? lookup_.cost_rows_low_vecs_[first_class].i8s[second_class]
: lookup_.cost_rows_high_vecs_[first_class].i8s[second_class - 16];
i16_t const if_substitution = scores_pre_substitution[0] + sub_cost;
i16_t const if_gap = sz_max_of_two(scores_pre_insertion[0], scores_pre_deletion[0]) + gap;
scores_new[0] = sz_max_of_two(sz_max_of_two(if_substitution, if_gap), (i16_t)0);
}
SZ_NOINLINE void score_slice_trampoline_( u8_t const *first_reversed_classes, u8_t const *second_classes, i16_t const *scores_pre_substitution, i16_t const *scores_pre_insertion, i16_t const *scores_pre_deletion, i16_t *scores_new, i16_t gap, size_t from, size_t to) const noexcept {
u256_vec_t gap_cost_vec;
gap_cost_vec.ymm = _mm256_set1_epi16(gap);
for (size_t idx_slice = from; idx_slice < to; ++idx_slice) {
size_t const progress = idx_slice * step_k;
slice_32cells( first_reversed_classes + progress, second_classes + progress, scores_pre_substitution + progress, scores_pre_insertion + progress, scores_pre_deletion + progress, scores_new + progress, gap_cost_vec);
}
}
template <typename executor_type_ = dummy_executor_t>
#if SZ_HAS_CONCEPTS_
requires executor_like<executor_type_>
#endif
void operator()( char const *first_reversed_slice, char const *second_slice, size_t const length, i16_t const *scores_pre_substitution, i16_t const *scores_pre_insertion, i16_t const *scores_pre_deletion, i16_t *scores_new, executor_type_ &&executor = {}) noexcept {
u8_t const *first_reversed_classes = (u8_t const *)first_reversed_slice;
u8_t const *second_classes = (u8_t const *)second_slice;
i16_t const gap = static_cast<i16_t>(this->gap_costs_.open_or_extend);
i16_t *const scores_new_begin = scores_new;
size_t const count_slices = length / step_k;
executor.for_slices(count_slices, [&](size_t from, size_t to) noexcept {
score_slice_trampoline_(first_reversed_classes, second_classes, scores_pre_substitution,
scores_pre_insertion, scores_pre_deletion, scores_new, gap, from, to);
});
for (size_t i = count_slices * step_k; i < length; ++i)
slice_1cell(first_reversed_classes + i, second_classes + i, scores_pre_substitution + i,
scores_pre_insertion + i, scores_pre_deletion + i, scores_new + i, gap);
i16_t best_in_diagonal = this->best_score_;
for (size_t i = 0; i != length; ++i) best_in_diagonal = sz_max_of_two(best_in_diagonal, scores_new_begin[i]);
this->best_score_ = best_in_diagonal;
}
};
template <sz_capability_t capability_>
struct tile_scorer<char const *, char const *, i32_t, error_costs_32x32_t, linear_gap_costs_t, sz_maximize_score_k,
sz_similarity_global_k, capability_, std::enable_if_t<(capability_ & sz_cap_haswell_k) != 0>>
: public tile_scorer<char const *, char const *, i32_t, error_costs_32x32_t, linear_gap_costs_t,
sz_maximize_score_k, sz_similarity_global_k, sz_cap_serial_k, void> {
using tile_scorer<char const *, char const *, i32_t, error_costs_32x32_t, linear_gap_costs_t, sz_maximize_score_k,
sz_similarity_global_k, sz_cap_serial_k, void>::tile_scorer;
static constexpr sz_similarity_objective_t objective_k = sz_maximize_score_k;
static constexpr sz_similarity_locality_t locality_k = sz_similarity_global_k;
static constexpr sz_capability_t capability_k = capability_;
static constexpr size_t step_k = 32;
substitution_lookup_haswell_t lookup_;
void prepare(bool transpose) noexcept {
lookup_.reload_costs(this->substituter_.class_substitution_costs, transpose);
}
SZ_INLINE void slice_32cells( u8_t const *first_reversed_slice, u8_t const *second_slice, i32_t const *scores_pre_substitution, i32_t const *scores_pre_insertion, i32_t const *scores_pre_deletion, i32_t *scores_new, u256_vec_t gap_cost_vec) const noexcept {
u256_vec_t first_vec, second_vec, cost_of_substitution_i8_vec;
u256_vec_t cost_of_substitution_i32_vecs[4];
u256_vec_t pre_substitution_vecs[4], pre_insert_vecs[4], pre_delete_vecs[4];
u256_vec_t cost_if_substitution_vecs[4], cost_if_gap_vecs[4], cell_score_vecs[4];
first_vec.ymm = _mm256_loadu_si256((__m256i const *)first_reversed_slice);
second_vec.ymm = _mm256_loadu_si256((__m256i const *)second_slice);
for (size_t part = 0; part != 4; ++part) {
pre_substitution_vecs[part].ymm = _mm256_loadu_si256((__m256i const *)(scores_pre_substitution + part * 8));
pre_insert_vecs[part].ymm = _mm256_loadu_si256((__m256i const *)(scores_pre_insertion + part * 8));
pre_delete_vecs[part].ymm = _mm256_loadu_si256((__m256i const *)(scores_pre_deletion + part * 8));
}
cost_of_substitution_i8_vec = lookup_.lookup32(first_vec, second_vec);
__m128i cost_low_xmm = _mm256_extracti128_si256(cost_of_substitution_i8_vec.ymm, 0);
__m128i cost_high_xmm = _mm256_extracti128_si256(cost_of_substitution_i8_vec.ymm, 1);
cost_of_substitution_i32_vecs[0].ymm = _mm256_cvtepi8_epi32(cost_low_xmm);
cost_of_substitution_i32_vecs[1].ymm = _mm256_cvtepi8_epi32(_mm_srli_si128(cost_low_xmm, 8));
cost_of_substitution_i32_vecs[2].ymm = _mm256_cvtepi8_epi32(cost_high_xmm);
cost_of_substitution_i32_vecs[3].ymm = _mm256_cvtepi8_epi32(_mm_srli_si128(cost_high_xmm, 8));
for (size_t part = 0; part != 4; ++part) {
cost_if_substitution_vecs[part].ymm = _mm256_add_epi32(pre_substitution_vecs[part].ymm,
cost_of_substitution_i32_vecs[part].ymm);
cost_if_gap_vecs[part].ymm = _mm256_add_epi32(
_mm256_max_epi32(pre_insert_vecs[part].ymm, pre_delete_vecs[part].ymm), gap_cost_vec.ymm);
cell_score_vecs[part].ymm = _mm256_max_epi32(cost_if_substitution_vecs[part].ymm,
cost_if_gap_vecs[part].ymm);
_mm256_storeu_si256((__m256i *)(scores_new + part * 8), cell_score_vecs[part].ymm);
}
}
SZ_INLINE void slice_1cell( u8_t const *first_reversed_slice, u8_t const *second_slice, i32_t const *scores_pre_substitution, i32_t const *scores_pre_insertion, i32_t const *scores_pre_deletion, i32_t *scores_new, i32_t gap) const noexcept {
u8_t const first_class = first_reversed_slice[0], second_class = second_slice[0];
i32_t const sub_cost = second_class < 16 ? lookup_.cost_rows_low_vecs_[first_class].i8s[second_class]
: lookup_.cost_rows_high_vecs_[first_class].i8s[second_class - 16];
i32_t const if_substitution = scores_pre_substitution[0] + sub_cost;
i32_t const if_gap = sz_max_of_two(scores_pre_insertion[0], scores_pre_deletion[0]) + gap;
scores_new[0] = sz_max_of_two(if_substitution, if_gap);
}
SZ_NOINLINE void score_slice_trampoline_( u8_t const *first_reversed_classes, u8_t const *second_classes, i32_t const *scores_pre_substitution, i32_t const *scores_pre_insertion, i32_t const *scores_pre_deletion, i32_t *scores_new, i32_t gap, size_t from, size_t to) const noexcept {
u256_vec_t gap_cost_vec;
gap_cost_vec.ymm = _mm256_set1_epi32(gap);
for (size_t idx_slice = from; idx_slice < to; ++idx_slice) {
size_t const progress = idx_slice * step_k;
slice_32cells( first_reversed_classes + progress, second_classes + progress, scores_pre_substitution + progress, scores_pre_insertion + progress, scores_pre_deletion + progress, scores_new + progress, gap_cost_vec);
}
}
template <typename executor_type_ = dummy_executor_t>
#if SZ_HAS_CONCEPTS_
requires executor_like<executor_type_>
#endif
void operator()( char const *first_reversed_slice, char const *second_slice, size_t const length, i32_t const *scores_pre_substitution, i32_t const *scores_pre_insertion, i32_t const *scores_pre_deletion, i32_t *scores_new, executor_type_ &&executor = {}) noexcept {
u8_t const *first_reversed_classes = (u8_t const *)first_reversed_slice;
u8_t const *second_classes = (u8_t const *)second_slice;
i32_t const gap = static_cast<i32_t>(this->gap_costs_.open_or_extend);
size_t const count_slices = length / step_k;
executor.for_slices(count_slices, [&](size_t from, size_t to) noexcept {
score_slice_trampoline_(first_reversed_classes, second_classes, scores_pre_substitution,
scores_pre_insertion, scores_pre_deletion, scores_new, gap, from, to);
});
for (size_t i = count_slices * step_k; i < length; ++i)
slice_1cell(first_reversed_classes + i, second_classes + i, scores_pre_substitution + i,
scores_pre_insertion + i, scores_pre_deletion + i, scores_new + i, gap);
this->last_score_ = scores_new[length - 1];
}
};
template <sz_capability_t capability_>
struct tile_scorer<char const *, char const *, i32_t, error_costs_32x32_t, linear_gap_costs_t, sz_maximize_score_k,
sz_similarity_local_k, capability_, std::enable_if_t<(capability_ & sz_cap_haswell_k) != 0>>
: public tile_scorer<char const *, char const *, i32_t, error_costs_32x32_t, linear_gap_costs_t,
sz_maximize_score_k, sz_similarity_local_k, sz_cap_serial_k, void> {
using tile_scorer<char const *, char const *, i32_t, error_costs_32x32_t, linear_gap_costs_t, sz_maximize_score_k,
sz_similarity_local_k, sz_cap_serial_k, void>::tile_scorer;
static constexpr sz_similarity_objective_t objective_k = sz_maximize_score_k;
static constexpr sz_similarity_locality_t locality_k = sz_similarity_local_k;
static constexpr sz_capability_t capability_k = capability_;
static constexpr size_t step_k = 32;
substitution_lookup_haswell_t lookup_;
void prepare(bool transpose) noexcept {
lookup_.reload_costs(this->substituter_.class_substitution_costs, transpose);
}
SZ_INLINE void slice_32cells( u8_t const *first_reversed_slice, u8_t const *second_slice, i32_t const *scores_pre_substitution, i32_t const *scores_pre_insertion, i32_t const *scores_pre_deletion, i32_t *scores_new, u256_vec_t gap_cost_vec) const noexcept {
u256_vec_t first_vec, second_vec, cost_of_substitution_i8_vec;
u256_vec_t cost_of_substitution_i32_vecs[4];
u256_vec_t pre_substitution_vecs[4], pre_insert_vecs[4], pre_delete_vecs[4];
u256_vec_t cost_if_substitution_vecs[4], cost_if_gap_vecs[4], cell_score_vecs[4];
first_vec.ymm = _mm256_loadu_si256((__m256i const *)first_reversed_slice);
second_vec.ymm = _mm256_loadu_si256((__m256i const *)second_slice);
for (size_t part = 0; part != 4; ++part) {
pre_substitution_vecs[part].ymm = _mm256_loadu_si256((__m256i const *)(scores_pre_substitution + part * 8));
pre_insert_vecs[part].ymm = _mm256_loadu_si256((__m256i const *)(scores_pre_insertion + part * 8));
pre_delete_vecs[part].ymm = _mm256_loadu_si256((__m256i const *)(scores_pre_deletion + part * 8));
}
cost_of_substitution_i8_vec = lookup_.lookup32(first_vec, second_vec);
__m128i cost_low_xmm = _mm256_extracti128_si256(cost_of_substitution_i8_vec.ymm, 0);
__m128i cost_high_xmm = _mm256_extracti128_si256(cost_of_substitution_i8_vec.ymm, 1);
cost_of_substitution_i32_vecs[0].ymm = _mm256_cvtepi8_epi32(cost_low_xmm);
cost_of_substitution_i32_vecs[1].ymm = _mm256_cvtepi8_epi32(_mm_srli_si128(cost_low_xmm, 8));
cost_of_substitution_i32_vecs[2].ymm = _mm256_cvtepi8_epi32(cost_high_xmm);
cost_of_substitution_i32_vecs[3].ymm = _mm256_cvtepi8_epi32(_mm_srli_si128(cost_high_xmm, 8));
for (size_t part = 0; part != 4; ++part) {
cost_if_substitution_vecs[part].ymm = _mm256_add_epi32(pre_substitution_vecs[part].ymm,
cost_of_substitution_i32_vecs[part].ymm);
cost_if_gap_vecs[part].ymm = _mm256_add_epi32(
_mm256_max_epi32(pre_insert_vecs[part].ymm, pre_delete_vecs[part].ymm), gap_cost_vec.ymm);
cell_score_vecs[part].ymm = _mm256_max_epi32(cost_if_substitution_vecs[part].ymm,
cost_if_gap_vecs[part].ymm);
cell_score_vecs[part].ymm = _mm256_max_epi32(cell_score_vecs[part].ymm, _mm256_setzero_si256());
_mm256_storeu_si256((__m256i *)(scores_new + part * 8), cell_score_vecs[part].ymm);
}
}
SZ_INLINE void slice_1cell( u8_t const *first_reversed_slice, u8_t const *second_slice, i32_t const *scores_pre_substitution, i32_t const *scores_pre_insertion, i32_t const *scores_pre_deletion, i32_t *scores_new, i32_t gap) const noexcept {
u8_t const first_class = first_reversed_slice[0], second_class = second_slice[0];
i32_t const sub_cost = second_class < 16 ? lookup_.cost_rows_low_vecs_[first_class].i8s[second_class]
: lookup_.cost_rows_high_vecs_[first_class].i8s[second_class - 16];
i32_t const if_substitution = scores_pre_substitution[0] + sub_cost;
i32_t const if_gap = sz_max_of_two(scores_pre_insertion[0], scores_pre_deletion[0]) + gap;
scores_new[0] = sz_max_of_two(sz_max_of_two(if_substitution, if_gap), (i32_t)0);
}
SZ_NOINLINE void score_slice_trampoline_( u8_t const *first_reversed_classes, u8_t const *second_classes, i32_t const *scores_pre_substitution, i32_t const *scores_pre_insertion, i32_t const *scores_pre_deletion, i32_t *scores_new, i32_t gap, size_t from, size_t to) const noexcept {
u256_vec_t gap_cost_vec;
gap_cost_vec.ymm = _mm256_set1_epi32(gap);
for (size_t idx_slice = from; idx_slice < to; ++idx_slice) {
size_t const progress = idx_slice * step_k;
slice_32cells( first_reversed_classes + progress, second_classes + progress, scores_pre_substitution + progress, scores_pre_insertion + progress, scores_pre_deletion + progress, scores_new + progress, gap_cost_vec);
}
}
template <typename executor_type_ = dummy_executor_t>
#if SZ_HAS_CONCEPTS_
requires executor_like<executor_type_>
#endif
void operator()( char const *first_reversed_slice, char const *second_slice, size_t const length, i32_t const *scores_pre_substitution, i32_t const *scores_pre_insertion, i32_t const *scores_pre_deletion, i32_t *scores_new, executor_type_ &&executor = {}) noexcept {
u8_t const *first_reversed_classes = (u8_t const *)first_reversed_slice;
u8_t const *second_classes = (u8_t const *)second_slice;
i32_t const gap = static_cast<i32_t>(this->gap_costs_.open_or_extend);
i32_t *const scores_new_begin = scores_new;
size_t const count_slices = length / step_k;
executor.for_slices(count_slices, [&](size_t from, size_t to) noexcept {
score_slice_trampoline_(first_reversed_classes, second_classes, scores_pre_substitution,
scores_pre_insertion, scores_pre_deletion, scores_new, gap, from, to);
});
for (size_t i = count_slices * step_k; i < length; ++i)
slice_1cell(first_reversed_classes + i, second_classes + i, scores_pre_substitution + i,
scores_pre_insertion + i, scores_pre_deletion + i, scores_new + i, gap);
i32_t best_in_diagonal = this->best_score_;
for (size_t i = 0; i != length; ++i) best_in_diagonal = sz_max_of_two(best_in_diagonal, scores_new_begin[i]);
this->best_score_ = best_in_diagonal;
}
};
template <sz_capability_t capability_>
struct tile_scorer<char const *, char const *, i16_t, error_costs_32x32_t, affine_gap_costs_t, sz_maximize_score_k,
sz_similarity_global_k, capability_, std::enable_if_t<(capability_ & sz_cap_haswell_k) != 0>>
: public tile_scorer<char const *, char const *, i16_t, error_costs_32x32_t, affine_gap_costs_t,
sz_maximize_score_k, sz_similarity_global_k, sz_cap_serial_k, void> {
using tile_scorer<char const *, char const *, i16_t, error_costs_32x32_t, affine_gap_costs_t, sz_maximize_score_k,
sz_similarity_global_k, sz_cap_serial_k, void>::tile_scorer;
static constexpr sz_similarity_objective_t objective_k = sz_maximize_score_k;
static constexpr sz_similarity_locality_t locality_k = sz_similarity_global_k;
static constexpr sz_capability_t capability_k = capability_;
static constexpr size_t step_k = 32;
substitution_lookup_haswell_t lookup_;
void prepare(bool transpose) noexcept {
lookup_.reload_costs(this->substituter_.class_substitution_costs, transpose);
}
SZ_INLINE void slice_32cells( u8_t const *first_reversed_slice, u8_t const *second_slice, i16_t const *scores_pre_substitution, i16_t const *scores_pre_insertion, i16_t const *scores_pre_deletion, i16_t const *scores_running_insertions, i16_t const *scores_running_deletions, i16_t *scores_new, i16_t *scores_new_insertions, i16_t *scores_new_deletions, u256_vec_t gap_open_vec, u256_vec_t gap_expand_vec) const noexcept {
u256_vec_t first_vec, second_vec;
u256_vec_t cost_of_substitution_i8_vec, cost_of_substitution_i16_vecs[2];
first_vec.ymm = _mm256_loadu_si256((__m256i const *)first_reversed_slice);
second_vec.ymm = _mm256_loadu_si256((__m256i const *)second_slice);
cost_of_substitution_i8_vec = lookup_.lookup32(first_vec, second_vec);
cost_of_substitution_i16_vecs[0].ymm = _mm256_cvtepi8_epi16(
_mm256_extracti128_si256(cost_of_substitution_i8_vec.ymm, 0));
cost_of_substitution_i16_vecs[1].ymm = _mm256_cvtepi8_epi16(
_mm256_extracti128_si256(cost_of_substitution_i8_vec.ymm, 1));
for (size_t part = 0; part != 2; ++part) {
size_t const offset = part * 16;
u256_vec_t pre_substitution, pre_insert_open, pre_delete_open, run_insert, run_delete;
u256_vec_t cost_if_insert, cost_if_delete, cell_score;
pre_substitution.ymm = _mm256_loadu_si256((__m256i const *)(scores_pre_substitution + offset));
pre_insert_open.ymm = _mm256_loadu_si256((__m256i const *)(scores_pre_insertion + offset));
pre_delete_open.ymm = _mm256_loadu_si256((__m256i const *)(scores_pre_deletion + offset));
run_insert.ymm = _mm256_loadu_si256((__m256i const *)(scores_running_insertions + offset));
run_delete.ymm = _mm256_loadu_si256((__m256i const *)(scores_running_deletions + offset));
cost_if_insert.ymm = _mm256_max_epi16(_mm256_add_epi16(run_insert.ymm, gap_expand_vec.ymm),
_mm256_add_epi16(pre_insert_open.ymm, gap_open_vec.ymm));
cost_if_delete.ymm = _mm256_max_epi16(_mm256_add_epi16(run_delete.ymm, gap_expand_vec.ymm),
_mm256_add_epi16(pre_delete_open.ymm, gap_open_vec.ymm));
cell_score.ymm = _mm256_max_epi16(
_mm256_add_epi16(pre_substitution.ymm, cost_of_substitution_i16_vecs[part].ymm),
_mm256_max_epi16(cost_if_insert.ymm, cost_if_delete.ymm));
_mm256_storeu_si256((__m256i *)(scores_new + offset), cell_score.ymm);
_mm256_storeu_si256((__m256i *)(scores_new_insertions + offset), cost_if_insert.ymm);
_mm256_storeu_si256((__m256i *)(scores_new_deletions + offset), cost_if_delete.ymm);
}
}
SZ_INLINE void slice_1cell( u8_t const *first_reversed_slice, u8_t const *second_slice, i16_t const *scores_pre_substitution, i16_t const *scores_pre_insertion, i16_t const *scores_pre_deletion, i16_t const *scores_running_insertions, i16_t const *scores_running_deletions, i16_t *scores_new, i16_t *scores_new_insertions, i16_t *scores_new_deletions, i16_t gap_open, i16_t gap_extend) const noexcept {
u8_t const first_class = first_reversed_slice[0], second_class = second_slice[0];
i16_t const sub_cost = second_class < 16 ? lookup_.cost_rows_low_vecs_[first_class].i8s[second_class]
: lookup_.cost_rows_high_vecs_[first_class].i8s[second_class - 16];
i16_t const if_insertion = sz_max_of_two((i16_t)(scores_running_insertions[0] + gap_extend),
(i16_t)(scores_pre_insertion[0] + gap_open));
i16_t const if_deletion = sz_max_of_two((i16_t)(scores_running_deletions[0] + gap_extend),
(i16_t)(scores_pre_deletion[0] + gap_open));
i16_t const if_substitution = scores_pre_substitution[0] + sub_cost;
scores_new[0] = sz_max_of_two(if_substitution, sz_max_of_two(if_insertion, if_deletion));
scores_new_insertions[0] = if_insertion;
scores_new_deletions[0] = if_deletion;
}
SZ_NOINLINE void score_slice_trampoline_( u8_t const *first_reversed_classes, u8_t const *second_classes, i16_t const *scores_pre_substitution, i16_t const *scores_pre_insertion, i16_t const *scores_pre_deletion, i16_t const *scores_running_insertions, i16_t const *scores_running_deletions, i16_t *scores_new, i16_t *scores_new_insertions, i16_t *scores_new_deletions, i16_t gap_open, i16_t gap_extend, size_t from, size_t to) const noexcept {
u256_vec_t gap_open_vec, gap_expand_vec;
gap_open_vec.ymm = _mm256_set1_epi16(gap_open);
gap_expand_vec.ymm = _mm256_set1_epi16(gap_extend);
for (size_t idx_slice = from; idx_slice < to; ++idx_slice) {
size_t const progress = idx_slice * step_k;
slice_32cells( first_reversed_classes + progress, second_classes + progress, scores_pre_substitution + progress, scores_pre_insertion + progress, scores_pre_deletion + progress, scores_running_insertions + progress, scores_running_deletions + progress, scores_new + progress, scores_new_insertions + progress, scores_new_deletions + progress, gap_open_vec, gap_expand_vec);
}
}
template <typename executor_type_ = dummy_executor_t>
#if SZ_HAS_CONCEPTS_
requires executor_like<executor_type_>
#endif
void operator()( char const *first_reversed_slice, char const *second_slice, size_t const length, i16_t const *scores_pre_substitution, i16_t const *scores_pre_insertion, i16_t const *scores_pre_deletion, i16_t const *scores_running_insertions, i16_t const *scores_running_deletions, i16_t *scores_new, i16_t *scores_new_insertions, i16_t *scores_new_deletions, executor_type_ &&executor = {}) noexcept {
u8_t const *first_reversed_classes = (u8_t const *)first_reversed_slice;
u8_t const *second_classes = (u8_t const *)second_slice;
i16_t const gap_open = static_cast<i16_t>(this->gap_costs_.open);
i16_t const gap_extend = static_cast<i16_t>(this->gap_costs_.extend);
size_t const count_slices = length / step_k;
executor.for_slices(count_slices, [&](size_t from, size_t to) noexcept {
score_slice_trampoline_(first_reversed_classes, second_classes, scores_pre_substitution,
scores_pre_insertion, scores_pre_deletion, scores_running_insertions,
scores_running_deletions, scores_new, scores_new_insertions, scores_new_deletions,
gap_open, gap_extend, from, to);
});
for (size_t i = count_slices * step_k; i < length; ++i)
slice_1cell( first_reversed_classes + i, second_classes + i, scores_pre_substitution + i, scores_pre_insertion + i, scores_pre_deletion + i, scores_running_insertions + i, scores_running_deletions + i, scores_new + i, scores_new_insertions + i, scores_new_deletions + i, gap_open, gap_extend);
this->last_score_ = scores_new[length - 1];
}
};
template <sz_capability_t capability_>
struct tile_scorer<char const *, char const *, i16_t, error_costs_32x32_t, affine_gap_costs_t, sz_maximize_score_k,
sz_similarity_local_k, capability_, std::enable_if_t<(capability_ & sz_cap_haswell_k) != 0>>
: public tile_scorer<char const *, char const *, i16_t, error_costs_32x32_t, affine_gap_costs_t,
sz_maximize_score_k, sz_similarity_local_k, sz_cap_serial_k, void> {
using tile_scorer<char const *, char const *, i16_t, error_costs_32x32_t, affine_gap_costs_t, sz_maximize_score_k,
sz_similarity_local_k, sz_cap_serial_k, void>::tile_scorer;
static constexpr sz_similarity_objective_t objective_k = sz_maximize_score_k;
static constexpr sz_similarity_locality_t locality_k = sz_similarity_local_k;
static constexpr sz_capability_t capability_k = capability_;
static constexpr size_t step_k = 32;
substitution_lookup_haswell_t lookup_;
void prepare(bool transpose) noexcept {
lookup_.reload_costs(this->substituter_.class_substitution_costs, transpose);
}
SZ_INLINE void slice_32cells( u8_t const *first_reversed_slice, u8_t const *second_slice, i16_t const *scores_pre_substitution, i16_t const *scores_pre_insertion, i16_t const *scores_pre_deletion, i16_t const *scores_running_insertions, i16_t const *scores_running_deletions, i16_t *scores_new, i16_t *scores_new_insertions, i16_t *scores_new_deletions, u256_vec_t gap_open_vec, u256_vec_t gap_expand_vec) const noexcept {
u256_vec_t first_vec, second_vec;
u256_vec_t cost_of_substitution_i8_vec, cost_of_substitution_i16_vecs[2];
first_vec.ymm = _mm256_loadu_si256((__m256i const *)first_reversed_slice);
second_vec.ymm = _mm256_loadu_si256((__m256i const *)second_slice);
cost_of_substitution_i8_vec = lookup_.lookup32(first_vec, second_vec);
cost_of_substitution_i16_vecs[0].ymm = _mm256_cvtepi8_epi16(
_mm256_extracti128_si256(cost_of_substitution_i8_vec.ymm, 0));
cost_of_substitution_i16_vecs[1].ymm = _mm256_cvtepi8_epi16(
_mm256_extracti128_si256(cost_of_substitution_i8_vec.ymm, 1));
for (size_t part = 0; part != 2; ++part) {
size_t const offset = part * 16;
u256_vec_t pre_substitution, pre_insert_open, pre_delete_open, run_insert, run_delete;
u256_vec_t cost_if_insert, cost_if_delete, cell_score;
pre_substitution.ymm = _mm256_loadu_si256((__m256i const *)(scores_pre_substitution + offset));
pre_insert_open.ymm = _mm256_loadu_si256((__m256i const *)(scores_pre_insertion + offset));
pre_delete_open.ymm = _mm256_loadu_si256((__m256i const *)(scores_pre_deletion + offset));
run_insert.ymm = _mm256_loadu_si256((__m256i const *)(scores_running_insertions + offset));
run_delete.ymm = _mm256_loadu_si256((__m256i const *)(scores_running_deletions + offset));
cost_if_insert.ymm = _mm256_max_epi16(_mm256_add_epi16(run_insert.ymm, gap_expand_vec.ymm),
_mm256_add_epi16(pre_insert_open.ymm, gap_open_vec.ymm));
cost_if_delete.ymm = _mm256_max_epi16(_mm256_add_epi16(run_delete.ymm, gap_expand_vec.ymm),
_mm256_add_epi16(pre_delete_open.ymm, gap_open_vec.ymm));
cell_score.ymm = _mm256_max_epi16(
_mm256_max_epi16(_mm256_add_epi16(pre_substitution.ymm, cost_of_substitution_i16_vecs[part].ymm),
_mm256_setzero_si256()),
_mm256_max_epi16(cost_if_insert.ymm, cost_if_delete.ymm));
_mm256_storeu_si256((__m256i *)(scores_new + offset), cell_score.ymm);
_mm256_storeu_si256((__m256i *)(scores_new_insertions + offset), cost_if_insert.ymm);
_mm256_storeu_si256((__m256i *)(scores_new_deletions + offset), cost_if_delete.ymm);
}
}
SZ_INLINE void slice_1cell( u8_t const *first_reversed_slice, u8_t const *second_slice, i16_t const *scores_pre_substitution, i16_t const *scores_pre_insertion, i16_t const *scores_pre_deletion, i16_t const *scores_running_insertions, i16_t const *scores_running_deletions, i16_t *scores_new, i16_t *scores_new_insertions, i16_t *scores_new_deletions, i16_t gap_open, i16_t gap_extend) const noexcept {
u8_t const first_class = first_reversed_slice[0], second_class = second_slice[0];
i16_t const sub_cost = second_class < 16 ? lookup_.cost_rows_low_vecs_[first_class].i8s[second_class]
: lookup_.cost_rows_high_vecs_[first_class].i8s[second_class - 16];
i16_t const if_insertion = sz_max_of_two((i16_t)(scores_running_insertions[0] + gap_extend),
(i16_t)(scores_pre_insertion[0] + gap_open));
i16_t const if_deletion = sz_max_of_two((i16_t)(scores_running_deletions[0] + gap_extend),
(i16_t)(scores_pre_deletion[0] + gap_open));
i16_t const if_substitution = sz_max_of_two((i16_t)(scores_pre_substitution[0] + sub_cost), (i16_t)0);
scores_new[0] = sz_max_of_two(if_substitution, sz_max_of_two(if_insertion, if_deletion));
scores_new_insertions[0] = if_insertion;
scores_new_deletions[0] = if_deletion;
}
SZ_NOINLINE void score_slice_trampoline_( u8_t const *first_reversed_classes, u8_t const *second_classes, i16_t const *scores_pre_substitution, i16_t const *scores_pre_insertion, i16_t const *scores_pre_deletion, i16_t const *scores_running_insertions, i16_t const *scores_running_deletions, i16_t *scores_new, i16_t *scores_new_insertions, i16_t *scores_new_deletions, i16_t gap_open, i16_t gap_extend, size_t from, size_t to) const noexcept {
u256_vec_t gap_open_vec, gap_expand_vec;
gap_open_vec.ymm = _mm256_set1_epi16(gap_open);
gap_expand_vec.ymm = _mm256_set1_epi16(gap_extend);
for (size_t idx_slice = from; idx_slice < to; ++idx_slice) {
size_t const progress = idx_slice * step_k;
slice_32cells( first_reversed_classes + progress, second_classes + progress, scores_pre_substitution + progress, scores_pre_insertion + progress, scores_pre_deletion + progress, scores_running_insertions + progress, scores_running_deletions + progress, scores_new + progress, scores_new_insertions + progress, scores_new_deletions + progress, gap_open_vec, gap_expand_vec);
}
}
template <typename executor_type_ = dummy_executor_t>
#if SZ_HAS_CONCEPTS_
requires executor_like<executor_type_>
#endif
void operator()( char const *first_reversed_slice, char const *second_slice, size_t const length, i16_t const *scores_pre_substitution, i16_t const *scores_pre_insertion, i16_t const *scores_pre_deletion, i16_t const *scores_running_insertions, i16_t const *scores_running_deletions, i16_t *scores_new, i16_t *scores_new_insertions, i16_t *scores_new_deletions, executor_type_ &&executor = {}) noexcept {
u8_t const *first_reversed_classes = (u8_t const *)first_reversed_slice;
u8_t const *second_classes = (u8_t const *)second_slice;
i16_t const gap_open = static_cast<i16_t>(this->gap_costs_.open);
i16_t const gap_extend = static_cast<i16_t>(this->gap_costs_.extend);
i16_t *const scores_new_begin = scores_new;
size_t const count_slices = length / step_k;
executor.for_slices(count_slices, [&](size_t from, size_t to) noexcept {
score_slice_trampoline_(first_reversed_classes, second_classes, scores_pre_substitution,
scores_pre_insertion, scores_pre_deletion, scores_running_insertions,
scores_running_deletions, scores_new, scores_new_insertions, scores_new_deletions,
gap_open, gap_extend, from, to);
});
for (size_t i = count_slices * step_k; i < length; ++i)
slice_1cell( first_reversed_classes + i, second_classes + i, scores_pre_substitution + i, scores_pre_insertion + i, scores_pre_deletion + i, scores_running_insertions + i, scores_running_deletions + i, scores_new + i, scores_new_insertions + i, scores_new_deletions + i, gap_open, gap_extend);
i16_t best_in_diagonal = this->best_score_;
for (size_t i = 0; i != length; ++i) best_in_diagonal = sz_max_of_two(best_in_diagonal, scores_new_begin[i]);
this->best_score_ = best_in_diagonal;
}
};
template <sz_capability_t capability_>
struct tile_scorer<char const *, char const *, i32_t, error_costs_32x32_t, affine_gap_costs_t, sz_maximize_score_k,
sz_similarity_global_k, capability_, std::enable_if_t<(capability_ & sz_cap_haswell_k) != 0>>
: public tile_scorer<char const *, char const *, i32_t, error_costs_32x32_t, affine_gap_costs_t,
sz_maximize_score_k, sz_similarity_global_k, sz_cap_serial_k, void> {
using tile_scorer<char const *, char const *, i32_t, error_costs_32x32_t, affine_gap_costs_t, sz_maximize_score_k,
sz_similarity_global_k, sz_cap_serial_k, void>::tile_scorer;
static constexpr sz_similarity_objective_t objective_k = sz_maximize_score_k;
static constexpr sz_similarity_locality_t locality_k = sz_similarity_global_k;
static constexpr sz_capability_t capability_k = capability_;
static constexpr size_t step_k = 32;
substitution_lookup_haswell_t lookup_;
void prepare(bool transpose) noexcept {
lookup_.reload_costs(this->substituter_.class_substitution_costs, transpose);
}
SZ_INLINE void slice_32cells( u8_t const *first_reversed_slice, u8_t const *second_slice, i32_t const *scores_pre_substitution, i32_t const *scores_pre_insertion, i32_t const *scores_pre_deletion, i32_t const *scores_running_insertions, i32_t const *scores_running_deletions, i32_t *scores_new, i32_t *scores_new_insertions, i32_t *scores_new_deletions, u256_vec_t gap_open_vec, u256_vec_t gap_expand_vec) const noexcept {
u256_vec_t first_vec, second_vec, cost_of_substitution_i8_vec;
u256_vec_t cost_of_substitution_i32_vecs[4];
first_vec.ymm = _mm256_loadu_si256((__m256i const *)first_reversed_slice);
second_vec.ymm = _mm256_loadu_si256((__m256i const *)second_slice);
cost_of_substitution_i8_vec = lookup_.lookup32(first_vec, second_vec);
__m128i cost_low_xmm = _mm256_extracti128_si256(cost_of_substitution_i8_vec.ymm, 0);
__m128i cost_high_xmm = _mm256_extracti128_si256(cost_of_substitution_i8_vec.ymm, 1);
cost_of_substitution_i32_vecs[0].ymm = _mm256_cvtepi8_epi32(cost_low_xmm);
cost_of_substitution_i32_vecs[1].ymm = _mm256_cvtepi8_epi32(_mm_srli_si128(cost_low_xmm, 8));
cost_of_substitution_i32_vecs[2].ymm = _mm256_cvtepi8_epi32(cost_high_xmm);
cost_of_substitution_i32_vecs[3].ymm = _mm256_cvtepi8_epi32(_mm_srli_si128(cost_high_xmm, 8));
for (size_t part = 0; part != 4; ++part) {
size_t const offset = part * 8;
u256_vec_t pre_substitution, pre_insert_open, pre_delete_open, run_insert, run_delete;
u256_vec_t cost_if_insert, cost_if_delete, cell_score;
pre_substitution.ymm = _mm256_loadu_si256((__m256i const *)(scores_pre_substitution + offset));
pre_insert_open.ymm = _mm256_loadu_si256((__m256i const *)(scores_pre_insertion + offset));
pre_delete_open.ymm = _mm256_loadu_si256((__m256i const *)(scores_pre_deletion + offset));
run_insert.ymm = _mm256_loadu_si256((__m256i const *)(scores_running_insertions + offset));
run_delete.ymm = _mm256_loadu_si256((__m256i const *)(scores_running_deletions + offset));
cost_if_insert.ymm = _mm256_max_epi32(_mm256_add_epi32(run_insert.ymm, gap_expand_vec.ymm),
_mm256_add_epi32(pre_insert_open.ymm, gap_open_vec.ymm));
cost_if_delete.ymm = _mm256_max_epi32(_mm256_add_epi32(run_delete.ymm, gap_expand_vec.ymm),
_mm256_add_epi32(pre_delete_open.ymm, gap_open_vec.ymm));
cell_score.ymm = _mm256_max_epi32(
_mm256_add_epi32(pre_substitution.ymm, cost_of_substitution_i32_vecs[part].ymm),
_mm256_max_epi32(cost_if_insert.ymm, cost_if_delete.ymm));
_mm256_storeu_si256((__m256i *)(scores_new + offset), cell_score.ymm);
_mm256_storeu_si256((__m256i *)(scores_new_insertions + offset), cost_if_insert.ymm);
_mm256_storeu_si256((__m256i *)(scores_new_deletions + offset), cost_if_delete.ymm);
}
}
SZ_INLINE void slice_1cell( u8_t const *first_reversed_slice, u8_t const *second_slice, i32_t const *scores_pre_substitution, i32_t const *scores_pre_insertion, i32_t const *scores_pre_deletion, i32_t const *scores_running_insertions, i32_t const *scores_running_deletions, i32_t *scores_new, i32_t *scores_new_insertions, i32_t *scores_new_deletions, i32_t gap_open, i32_t gap_extend) const noexcept {
u8_t const first_class = first_reversed_slice[0], second_class = second_slice[0];
i32_t const sub_cost = second_class < 16 ? lookup_.cost_rows_low_vecs_[first_class].i8s[second_class]
: lookup_.cost_rows_high_vecs_[first_class].i8s[second_class - 16];
i32_t const if_insertion = sz_max_of_two(scores_running_insertions[0] + gap_extend,
scores_pre_insertion[0] + gap_open);
i32_t const if_deletion = sz_max_of_two(scores_running_deletions[0] + gap_extend,
scores_pre_deletion[0] + gap_open);
i32_t const if_substitution = scores_pre_substitution[0] + sub_cost;
scores_new[0] = sz_max_of_two(if_substitution, sz_max_of_two(if_insertion, if_deletion));
scores_new_insertions[0] = if_insertion;
scores_new_deletions[0] = if_deletion;
}
SZ_NOINLINE void score_slice_trampoline_( u8_t const *first_reversed_classes, u8_t const *second_classes, i32_t const *scores_pre_substitution, i32_t const *scores_pre_insertion, i32_t const *scores_pre_deletion, i32_t const *scores_running_insertions, i32_t const *scores_running_deletions, i32_t *scores_new, i32_t *scores_new_insertions, i32_t *scores_new_deletions, i32_t gap_open, i32_t gap_extend, size_t from, size_t to) const noexcept {
u256_vec_t gap_open_vec, gap_expand_vec;
gap_open_vec.ymm = _mm256_set1_epi32(gap_open);
gap_expand_vec.ymm = _mm256_set1_epi32(gap_extend);
for (size_t idx_slice = from; idx_slice < to; ++idx_slice) {
size_t const progress = idx_slice * step_k;
slice_32cells( first_reversed_classes + progress, second_classes + progress, scores_pre_substitution + progress, scores_pre_insertion + progress, scores_pre_deletion + progress, scores_running_insertions + progress, scores_running_deletions + progress, scores_new + progress, scores_new_insertions + progress, scores_new_deletions + progress, gap_open_vec, gap_expand_vec);
}
}
template <typename executor_type_ = dummy_executor_t>
#if SZ_HAS_CONCEPTS_
requires executor_like<executor_type_>
#endif
void operator()( char const *first_reversed_slice, char const *second_slice, size_t const length, i32_t const *scores_pre_substitution, i32_t const *scores_pre_insertion, i32_t const *scores_pre_deletion, i32_t const *scores_running_insertions, i32_t const *scores_running_deletions, i32_t *scores_new, i32_t *scores_new_insertions, i32_t *scores_new_deletions, executor_type_ &&executor = {}) noexcept {
u8_t const *first_reversed_classes = (u8_t const *)first_reversed_slice;
u8_t const *second_classes = (u8_t const *)second_slice;
i32_t const gap_open = static_cast<i32_t>(this->gap_costs_.open);
i32_t const gap_extend = static_cast<i32_t>(this->gap_costs_.extend);
size_t const count_slices = length / step_k;
executor.for_slices(count_slices, [&](size_t from, size_t to) noexcept {
score_slice_trampoline_(first_reversed_classes, second_classes, scores_pre_substitution,
scores_pre_insertion, scores_pre_deletion, scores_running_insertions,
scores_running_deletions, scores_new, scores_new_insertions, scores_new_deletions,
gap_open, gap_extend, from, to);
});
for (size_t i = count_slices * step_k; i < length; ++i)
slice_1cell( first_reversed_classes + i, second_classes + i, scores_pre_substitution + i, scores_pre_insertion + i, scores_pre_deletion + i, scores_running_insertions + i, scores_running_deletions + i, scores_new + i, scores_new_insertions + i, scores_new_deletions + i, gap_open, gap_extend);
this->last_score_ = scores_new[length - 1];
}
};
template <sz_capability_t capability_>
struct tile_scorer<char const *, char const *, i32_t, error_costs_32x32_t, affine_gap_costs_t, sz_maximize_score_k,
sz_similarity_local_k, capability_, std::enable_if_t<(capability_ & sz_cap_haswell_k) != 0>>
: public tile_scorer<char const *, char const *, i32_t, error_costs_32x32_t, affine_gap_costs_t,
sz_maximize_score_k, sz_similarity_local_k, sz_cap_serial_k, void> {
using tile_scorer<char const *, char const *, i32_t, error_costs_32x32_t, affine_gap_costs_t, sz_maximize_score_k,
sz_similarity_local_k, sz_cap_serial_k, void>::tile_scorer;
static constexpr sz_similarity_objective_t objective_k = sz_maximize_score_k;
static constexpr sz_similarity_locality_t locality_k = sz_similarity_local_k;
static constexpr sz_capability_t capability_k = capability_;
static constexpr size_t step_k = 32;
substitution_lookup_haswell_t lookup_;
void prepare(bool transpose) noexcept {
lookup_.reload_costs(this->substituter_.class_substitution_costs, transpose);
}
SZ_INLINE void slice_32cells( u8_t const *first_reversed_slice, u8_t const *second_slice, i32_t const *scores_pre_substitution, i32_t const *scores_pre_insertion, i32_t const *scores_pre_deletion, i32_t const *scores_running_insertions, i32_t const *scores_running_deletions, i32_t *scores_new, i32_t *scores_new_insertions, i32_t *scores_new_deletions, u256_vec_t gap_open_vec, u256_vec_t gap_expand_vec) const noexcept {
u256_vec_t first_vec, second_vec, cost_of_substitution_i8_vec;
u256_vec_t cost_of_substitution_i32_vecs[4];
first_vec.ymm = _mm256_loadu_si256((__m256i const *)first_reversed_slice);
second_vec.ymm = _mm256_loadu_si256((__m256i const *)second_slice);
cost_of_substitution_i8_vec = lookup_.lookup32(first_vec, second_vec);
__m128i cost_low_xmm = _mm256_extracti128_si256(cost_of_substitution_i8_vec.ymm, 0);
__m128i cost_high_xmm = _mm256_extracti128_si256(cost_of_substitution_i8_vec.ymm, 1);
cost_of_substitution_i32_vecs[0].ymm = _mm256_cvtepi8_epi32(cost_low_xmm);
cost_of_substitution_i32_vecs[1].ymm = _mm256_cvtepi8_epi32(_mm_srli_si128(cost_low_xmm, 8));
cost_of_substitution_i32_vecs[2].ymm = _mm256_cvtepi8_epi32(cost_high_xmm);
cost_of_substitution_i32_vecs[3].ymm = _mm256_cvtepi8_epi32(_mm_srli_si128(cost_high_xmm, 8));
for (size_t part = 0; part != 4; ++part) {
size_t const offset = part * 8;
u256_vec_t pre_substitution, pre_insert_open, pre_delete_open, run_insert, run_delete;
u256_vec_t cost_if_insert, cost_if_delete, cell_score;
pre_substitution.ymm = _mm256_loadu_si256((__m256i const *)(scores_pre_substitution + offset));
pre_insert_open.ymm = _mm256_loadu_si256((__m256i const *)(scores_pre_insertion + offset));
pre_delete_open.ymm = _mm256_loadu_si256((__m256i const *)(scores_pre_deletion + offset));
run_insert.ymm = _mm256_loadu_si256((__m256i const *)(scores_running_insertions + offset));
run_delete.ymm = _mm256_loadu_si256((__m256i const *)(scores_running_deletions + offset));
cost_if_insert.ymm = _mm256_max_epi32(_mm256_add_epi32(run_insert.ymm, gap_expand_vec.ymm),
_mm256_add_epi32(pre_insert_open.ymm, gap_open_vec.ymm));
cost_if_delete.ymm = _mm256_max_epi32(_mm256_add_epi32(run_delete.ymm, gap_expand_vec.ymm),
_mm256_add_epi32(pre_delete_open.ymm, gap_open_vec.ymm));
cell_score.ymm = _mm256_max_epi32(
_mm256_max_epi32(_mm256_add_epi32(pre_substitution.ymm, cost_of_substitution_i32_vecs[part].ymm),
_mm256_setzero_si256()),
_mm256_max_epi32(cost_if_insert.ymm, cost_if_delete.ymm));
_mm256_storeu_si256((__m256i *)(scores_new + offset), cell_score.ymm);
_mm256_storeu_si256((__m256i *)(scores_new_insertions + offset), cost_if_insert.ymm);
_mm256_storeu_si256((__m256i *)(scores_new_deletions + offset), cost_if_delete.ymm);
}
}
SZ_INLINE void slice_1cell( u8_t const *first_reversed_slice, u8_t const *second_slice, i32_t const *scores_pre_substitution, i32_t const *scores_pre_insertion, i32_t const *scores_pre_deletion, i32_t const *scores_running_insertions, i32_t const *scores_running_deletions, i32_t *scores_new, i32_t *scores_new_insertions, i32_t *scores_new_deletions, i32_t gap_open, i32_t gap_extend) const noexcept {
u8_t const first_class = first_reversed_slice[0], second_class = second_slice[0];
i32_t const sub_cost = second_class < 16 ? lookup_.cost_rows_low_vecs_[first_class].i8s[second_class]
: lookup_.cost_rows_high_vecs_[first_class].i8s[second_class - 16];
i32_t const if_insertion = sz_max_of_two(scores_running_insertions[0] + gap_extend,
scores_pre_insertion[0] + gap_open);
i32_t const if_deletion = sz_max_of_two(scores_running_deletions[0] + gap_extend,
scores_pre_deletion[0] + gap_open);
i32_t const if_substitution = sz_max_of_two(scores_pre_substitution[0] + sub_cost, 0);
scores_new[0] = sz_max_of_two(if_substitution, sz_max_of_two(if_insertion, if_deletion));
scores_new_insertions[0] = if_insertion;
scores_new_deletions[0] = if_deletion;
}
SZ_NOINLINE void score_slice_trampoline_( u8_t const *first_reversed_classes, u8_t const *second_classes, i32_t const *scores_pre_substitution, i32_t const *scores_pre_insertion, i32_t const *scores_pre_deletion, i32_t const *scores_running_insertions, i32_t const *scores_running_deletions, i32_t *scores_new, i32_t *scores_new_insertions, i32_t *scores_new_deletions, i32_t gap_open, i32_t gap_extend, size_t from, size_t to) const noexcept {
u256_vec_t gap_open_vec, gap_expand_vec;
gap_open_vec.ymm = _mm256_set1_epi32(gap_open);
gap_expand_vec.ymm = _mm256_set1_epi32(gap_extend);
for (size_t idx_slice = from; idx_slice < to; ++idx_slice) {
size_t const progress = idx_slice * step_k;
slice_32cells( first_reversed_classes + progress, second_classes + progress, scores_pre_substitution + progress, scores_pre_insertion + progress, scores_pre_deletion + progress, scores_running_insertions + progress, scores_running_deletions + progress, scores_new + progress, scores_new_insertions + progress, scores_new_deletions + progress, gap_open_vec, gap_expand_vec);
}
}
template <typename executor_type_ = dummy_executor_t>
#if SZ_HAS_CONCEPTS_
requires executor_like<executor_type_>
#endif
void operator()( char const *first_reversed_slice, char const *second_slice, size_t const length, i32_t const *scores_pre_substitution, i32_t const *scores_pre_insertion, i32_t const *scores_pre_deletion, i32_t const *scores_running_insertions, i32_t const *scores_running_deletions, i32_t *scores_new, i32_t *scores_new_insertions, i32_t *scores_new_deletions, executor_type_ &&executor = {}) noexcept {
u8_t const *first_reversed_classes = (u8_t const *)first_reversed_slice;
u8_t const *second_classes = (u8_t const *)second_slice;
i32_t const gap_open = static_cast<i32_t>(this->gap_costs_.open);
i32_t const gap_extend = static_cast<i32_t>(this->gap_costs_.extend);
i32_t *const scores_new_begin = scores_new;
size_t const count_slices = length / step_k;
executor.for_slices(count_slices, [&](size_t from, size_t to) noexcept {
score_slice_trampoline_(first_reversed_classes, second_classes, scores_pre_substitution,
scores_pre_insertion, scores_pre_deletion, scores_running_insertions,
scores_running_deletions, scores_new, scores_new_insertions, scores_new_deletions,
gap_open, gap_extend, from, to);
});
for (size_t i = count_slices * step_k; i < length; ++i)
slice_1cell( first_reversed_classes + i, second_classes + i, scores_pre_substitution + i, scores_pre_insertion + i, scores_pre_deletion + i, scores_running_insertions + i, scores_running_deletions + i, scores_new + i, scores_new_insertions + i, scores_new_deletions + i, gap_open, gap_extend);
i32_t best_in_diagonal = this->best_score_;
for (size_t i = 0; i != length; ++i) best_in_diagonal = sz_max_of_two(best_in_diagonal, scores_new_begin[i]);
this->best_score_ = best_in_diagonal;
}
};
#pragma region Uniform Cost Levenshtein
template <typename first_iterator_type_, typename second_iterator_type_, typename score_type_, typename gap_costs_type_,
sz_capability_t capability_>
struct tile_scorer<first_iterator_type_, second_iterator_type_, score_type_, uniform_substitution_costs_t,
gap_costs_type_, sz_minimize_distance_k, sz_similarity_global_k, capability_,
std::enable_if_t<(capability_ & sz_cap_haswell_k) != 0>>
: public tile_scorer<first_iterator_type_, second_iterator_type_, score_type_, uniform_substitution_costs_t,
gap_costs_type_, sz_minimize_distance_k, sz_similarity_global_k, sz_cap_serial_k, void> {
using base_t = tile_scorer<first_iterator_type_, second_iterator_type_, score_type_, uniform_substitution_costs_t,
gap_costs_type_, sz_minimize_distance_k, sz_similarity_global_k, sz_cap_serial_k, void>;
using base_t::base_t;
using base_t::operator();
};
template <sz_capability_t capability_>
struct tile_scorer<char const *, char const *, u16_t, uniform_substitution_costs_t, linear_gap_costs_t,
sz_minimize_distance_k, sz_similarity_global_k, capability_,
std::enable_if_t<(capability_ & sz_cap_haswell_k) != 0>>
: public tile_scorer<char const *, char const *, u16_t, uniform_substitution_costs_t, linear_gap_costs_t,
sz_minimize_distance_k, sz_similarity_global_k, sz_cap_serial_k, void> {
using tile_scorer<char const *, char const *, u16_t, uniform_substitution_costs_t, linear_gap_costs_t,
sz_minimize_distance_k, sz_similarity_global_k, sz_cap_serial_k, void>::tile_scorer;
static constexpr sz_similarity_objective_t objective_k = sz_minimize_distance_k;
static constexpr sz_similarity_locality_t locality_k = sz_similarity_global_k;
static constexpr sz_capability_t capability_k = capability_;
static constexpr size_t step_k = 32;
SZ_INLINE void slice_32cells( u8_t const *first_reversed_slice, u8_t const *second_slice, u16_t const *scores_pre_substitution, u16_t const *scores_pre_insertion, u16_t const *scores_pre_deletion, u16_t *scores_new, __m256i match_cost_u8_vec, __m256i mismatch_cost_u8_vec, __m256i gap_cost_vec) const noexcept {
__m256i first_vec = _mm256_loadu_si256((__m256i const *)first_reversed_slice);
__m256i second_vec = _mm256_loadu_si256((__m256i const *)second_slice);
__m256i equal_vec = _mm256_cmpeq_epi8(first_vec, second_vec);
__m256i cost_u8_vec = _mm256_blendv_epi8(mismatch_cost_u8_vec, match_cost_u8_vec, equal_vec);
__m256i cost_u16_vecs[2];
cost_u16_vecs[0] = _mm256_cvtepu8_epi16(_mm256_extracti128_si256(cost_u8_vec, 0));
cost_u16_vecs[1] = _mm256_cvtepu8_epi16(_mm256_extracti128_si256(cost_u8_vec, 1));
for (size_t part = 0; part != 2; ++part) {
__m256i pre_substitution_vec = _mm256_loadu_si256((__m256i const *)(scores_pre_substitution + part * 16));
__m256i pre_insert_vec = _mm256_loadu_si256((__m256i const *)(scores_pre_insertion + part * 16));
__m256i pre_delete_vec = _mm256_loadu_si256((__m256i const *)(scores_pre_deletion + part * 16));
__m256i cost_if_substitution_vec = _mm256_add_epi16(pre_substitution_vec, cost_u16_vecs[part]);
__m256i cost_if_gap_vec = _mm256_add_epi16(_mm256_min_epu16(pre_insert_vec, pre_delete_vec), gap_cost_vec);
__m256i cell_score_vec = _mm256_min_epu16(cost_if_substitution_vec, cost_if_gap_vec);
_mm256_storeu_si256((__m256i *)(scores_new + part * 16), cell_score_vec);
}
}
SZ_INLINE void slice_1cell( u8_t const *first_reversed_slice, u8_t const *second_slice, size_t i, u16_t const *scores_pre_substitution, u16_t const *scores_pre_insertion, u16_t const *scores_pre_deletion, u16_t *scores_new, u16_t gap) const noexcept {
u16_t const cost = first_reversed_slice[i] == second_slice[i] ? (u16_t)this->substituter_.match
: (u16_t)this->substituter_.mismatch;
u16_t const if_substitution = (u16_t)(scores_pre_substitution[i] + cost);
u16_t const if_gap = (u16_t)(sz_min_of_two(scores_pre_insertion[i], scores_pre_deletion[i]) + gap);
scores_new[i] = sz_min_of_two(if_substitution, if_gap);
}
SZ_NOINLINE void score_slice_trampoline_( u8_t const *first_reversed_slice, u8_t const *second_slice, u16_t const *scores_pre_substitution, u16_t const *scores_pre_insertion, u16_t const *scores_pre_deletion, u16_t *scores_new, __m256i match_cost_u8_vec, __m256i mismatch_cost_u8_vec, __m256i gap_cost_vec, size_t from, size_t to) const noexcept {
for (size_t idx_slice = from; idx_slice < to; ++idx_slice) {
size_t const progress = idx_slice * step_k;
slice_32cells(first_reversed_slice + progress, second_slice + progress, scores_pre_substitution + progress,
scores_pre_insertion + progress, scores_pre_deletion + progress, scores_new + progress,
match_cost_u8_vec, mismatch_cost_u8_vec, gap_cost_vec);
}
}
template <typename executor_type_ = dummy_executor_t>
#if SZ_HAS_CONCEPTS_
requires executor_like<executor_type_>
#endif
void operator()( char const *first_reversed_slice, char const *second_slice, size_t const length, u16_t const *scores_pre_substitution, u16_t const *scores_pre_insertion, u16_t const *scores_pre_deletion, u16_t *scores_new, executor_type_ &&executor = {}) noexcept {
u8_t const *first_reversed = (u8_t const *)first_reversed_slice;
u8_t const *second = (u8_t const *)second_slice;
u16_t const gap = static_cast<u16_t>(this->gap_costs_.open_or_extend);
__m256i const match_cost_u8_vec = _mm256_set1_epi8((char)this->substituter_.match);
__m256i const mismatch_cost_u8_vec = _mm256_set1_epi8((char)this->substituter_.mismatch);
__m256i const gap_cost_vec = _mm256_set1_epi16((short)gap);
size_t const count_slices = length / step_k;
executor.for_slices(count_slices, [&](size_t from, size_t to) noexcept {
score_slice_trampoline_(first_reversed, second, scores_pre_substitution, scores_pre_insertion,
scores_pre_deletion, scores_new, match_cost_u8_vec, mismatch_cost_u8_vec,
gap_cost_vec, from, to);
});
for (size_t i = count_slices * step_k; i < length; ++i)
slice_1cell(first_reversed, second, i, scores_pre_substitution, scores_pre_insertion, scores_pre_deletion,
scores_new, gap);
this->last_score_ = scores_new[length - 1];
}
};
template <sz_capability_t capability_>
struct tile_scorer<char const *, char const *, u32_t, uniform_substitution_costs_t, linear_gap_costs_t,
sz_minimize_distance_k, sz_similarity_global_k, capability_,
std::enable_if_t<(capability_ & sz_cap_haswell_k) != 0>>
: public tile_scorer<char const *, char const *, u32_t, uniform_substitution_costs_t, linear_gap_costs_t,
sz_minimize_distance_k, sz_similarity_global_k, sz_cap_serial_k, void> {
using tile_scorer<char const *, char const *, u32_t, uniform_substitution_costs_t, linear_gap_costs_t,
sz_minimize_distance_k, sz_similarity_global_k, sz_cap_serial_k, void>::tile_scorer;
static constexpr sz_similarity_objective_t objective_k = sz_minimize_distance_k;
static constexpr sz_similarity_locality_t locality_k = sz_similarity_global_k;
static constexpr sz_capability_t capability_k = capability_;
static constexpr size_t step_k = 32;
SZ_INLINE void slice_32cells( u8_t const *first_reversed_slice, u8_t const *second_slice, u32_t const *scores_pre_substitution, u32_t const *scores_pre_insertion, u32_t const *scores_pre_deletion, u32_t *scores_new, __m256i match_cost_u8_vec, __m256i mismatch_cost_u8_vec, __m256i gap_cost_vec) const noexcept {
__m256i first_vec = _mm256_loadu_si256((__m256i const *)first_reversed_slice);
__m256i second_vec = _mm256_loadu_si256((__m256i const *)second_slice);
__m256i equal_vec = _mm256_cmpeq_epi8(first_vec, second_vec);
__m256i cost_u8_vec = _mm256_blendv_epi8(mismatch_cost_u8_vec, match_cost_u8_vec, equal_vec);
__m128i cost_low_xmm = _mm256_extracti128_si256(cost_u8_vec, 0);
__m128i cost_high_xmm = _mm256_extracti128_si256(cost_u8_vec, 1);
__m256i cost_u32_vecs[4];
cost_u32_vecs[0] = _mm256_cvtepu8_epi32(cost_low_xmm);
cost_u32_vecs[1] = _mm256_cvtepu8_epi32(_mm_srli_si128(cost_low_xmm, 8));
cost_u32_vecs[2] = _mm256_cvtepu8_epi32(cost_high_xmm);
cost_u32_vecs[3] = _mm256_cvtepu8_epi32(_mm_srli_si128(cost_high_xmm, 8));
for (size_t part = 0; part != 4; ++part) {
__m256i pre_substitution_vec = _mm256_loadu_si256((__m256i const *)(scores_pre_substitution + part * 8));
__m256i pre_insert_vec = _mm256_loadu_si256((__m256i const *)(scores_pre_insertion + part * 8));
__m256i pre_delete_vec = _mm256_loadu_si256((__m256i const *)(scores_pre_deletion + part * 8));
__m256i cost_if_substitution_vec = _mm256_add_epi32(pre_substitution_vec, cost_u32_vecs[part]);
__m256i cost_if_gap_vec = _mm256_add_epi32(_mm256_min_epu32(pre_insert_vec, pre_delete_vec), gap_cost_vec);
__m256i cell_score_vec = _mm256_min_epu32(cost_if_substitution_vec, cost_if_gap_vec);
_mm256_storeu_si256((__m256i *)(scores_new + part * 8), cell_score_vec);
}
}
SZ_INLINE void slice_1cell( u8_t const *first_reversed_slice, u8_t const *second_slice, size_t i, u32_t const *scores_pre_substitution, u32_t const *scores_pre_insertion, u32_t const *scores_pre_deletion, u32_t *scores_new, u32_t gap) const noexcept {
u32_t const cost = first_reversed_slice[i] == second_slice[i] ? (u32_t)this->substituter_.match
: (u32_t)this->substituter_.mismatch;
u32_t const if_substitution = scores_pre_substitution[i] + cost;
u32_t const if_gap = sz_min_of_two(scores_pre_insertion[i], scores_pre_deletion[i]) + gap;
scores_new[i] = sz_min_of_two(if_substitution, if_gap);
}
SZ_NOINLINE void score_slice_trampoline_( u8_t const *first_reversed_slice, u8_t const *second_slice, u32_t const *scores_pre_substitution, u32_t const *scores_pre_insertion, u32_t const *scores_pre_deletion, u32_t *scores_new, __m256i match_cost_u8_vec, __m256i mismatch_cost_u8_vec, __m256i gap_cost_vec, size_t from, size_t to) const noexcept {
for (size_t idx_slice = from; idx_slice < to; ++idx_slice) {
size_t const progress = idx_slice * step_k;
slice_32cells(first_reversed_slice + progress, second_slice + progress, scores_pre_substitution + progress,
scores_pre_insertion + progress, scores_pre_deletion + progress, scores_new + progress,
match_cost_u8_vec, mismatch_cost_u8_vec, gap_cost_vec);
}
}
template <typename executor_type_ = dummy_executor_t>
#if SZ_HAS_CONCEPTS_
requires executor_like<executor_type_>
#endif
void operator()( char const *first_reversed_slice, char const *second_slice, size_t const length, u32_t const *scores_pre_substitution, u32_t const *scores_pre_insertion, u32_t const *scores_pre_deletion, u32_t *scores_new, executor_type_ &&executor = {}) noexcept {
u8_t const *first_reversed = (u8_t const *)first_reversed_slice;
u8_t const *second = (u8_t const *)second_slice;
u32_t const gap = static_cast<u32_t>(this->gap_costs_.open_or_extend);
__m256i const match_cost_u8_vec = _mm256_set1_epi8((char)this->substituter_.match);
__m256i const mismatch_cost_u8_vec = _mm256_set1_epi8((char)this->substituter_.mismatch);
__m256i const gap_cost_vec = _mm256_set1_epi32((int)gap);
size_t const count_slices = length / step_k;
executor.for_slices(count_slices, [&](size_t from, size_t to) noexcept {
score_slice_trampoline_(first_reversed, second, scores_pre_substitution, scores_pre_insertion,
scores_pre_deletion, scores_new, match_cost_u8_vec, mismatch_cost_u8_vec,
gap_cost_vec, from, to);
});
for (size_t i = count_slices * step_k; i < length; ++i)
slice_1cell(first_reversed, second, i, scores_pre_substitution, scores_pre_insertion, scores_pre_deletion,
scores_new, gap);
this->last_score_ = scores_new[length - 1];
}
};
template <sz_capability_t capability_>
struct tile_scorer<char const *, char const *, u16_t, uniform_substitution_costs_t, affine_gap_costs_t,
sz_minimize_distance_k, sz_similarity_global_k, capability_,
std::enable_if_t<(capability_ & sz_cap_haswell_k) != 0>>
: public tile_scorer<char const *, char const *, u16_t, uniform_substitution_costs_t, affine_gap_costs_t,
sz_minimize_distance_k, sz_similarity_global_k, sz_cap_serial_k, void> {
using tile_scorer<char const *, char const *, u16_t, uniform_substitution_costs_t, affine_gap_costs_t,
sz_minimize_distance_k, sz_similarity_global_k, sz_cap_serial_k, void>::tile_scorer;
static constexpr sz_similarity_objective_t objective_k = sz_minimize_distance_k;
static constexpr sz_similarity_locality_t locality_k = sz_similarity_global_k;
static constexpr sz_capability_t capability_k = capability_;
static constexpr size_t step_k = 32;
SZ_INLINE void slice_32cells( u8_t const *first_reversed_slice, u8_t const *second_slice, u16_t const *scores_pre_substitution, u16_t const *scores_pre_insertion, u16_t const *scores_pre_deletion, u16_t const *scores_running_insertions, u16_t const *scores_running_deletions, u16_t *scores_new, u16_t *scores_new_insertions, u16_t *scores_new_deletions, __m256i match_cost_u8_vec, __m256i mismatch_cost_u8_vec, __m256i gap_open_vec, __m256i gap_extend_vec) const noexcept {
__m256i first_vec = _mm256_loadu_si256((__m256i const *)first_reversed_slice);
__m256i second_vec = _mm256_loadu_si256((__m256i const *)second_slice);
__m256i equal_vec = _mm256_cmpeq_epi8(first_vec, second_vec);
__m256i cost_u8_vec = _mm256_blendv_epi8(mismatch_cost_u8_vec, match_cost_u8_vec, equal_vec);
__m256i cost_u16_vecs[2];
cost_u16_vecs[0] = _mm256_cvtepu8_epi16(_mm256_extracti128_si256(cost_u8_vec, 0));
cost_u16_vecs[1] = _mm256_cvtepu8_epi16(_mm256_extracti128_si256(cost_u8_vec, 1));
for (size_t part = 0; part != 2; ++part) {
size_t const offset = part * 16;
__m256i pre_substitution_vec = _mm256_loadu_si256((__m256i const *)(scores_pre_substitution + offset));
__m256i pre_insert_open_vec = _mm256_loadu_si256((__m256i const *)(scores_pre_insertion + offset));
__m256i pre_delete_open_vec = _mm256_loadu_si256((__m256i const *)(scores_pre_deletion + offset));
__m256i run_insert_vec = _mm256_loadu_si256((__m256i const *)(scores_running_insertions + offset));
__m256i run_delete_vec = _mm256_loadu_si256((__m256i const *)(scores_running_deletions + offset));
__m256i cost_if_insert_vec = _mm256_min_epu16(_mm256_add_epi16(pre_insert_open_vec, gap_open_vec),
_mm256_add_epi16(run_insert_vec, gap_extend_vec));
__m256i cost_if_delete_vec = _mm256_min_epu16(_mm256_add_epi16(pre_delete_open_vec, gap_open_vec),
_mm256_add_epi16(run_delete_vec, gap_extend_vec));
__m256i cost_if_substitution_vec = _mm256_add_epi16(pre_substitution_vec, cost_u16_vecs[part]);
__m256i cell_score_vec = _mm256_min_epu16(_mm256_min_epu16(cost_if_insert_vec, cost_if_delete_vec),
cost_if_substitution_vec);
_mm256_storeu_si256((__m256i *)(scores_new + offset), cell_score_vec);
_mm256_storeu_si256((__m256i *)(scores_new_insertions + offset), cost_if_insert_vec);
_mm256_storeu_si256((__m256i *)(scores_new_deletions + offset), cost_if_delete_vec);
}
}
SZ_INLINE void slice_1cell( u8_t const *first_reversed_slice, u8_t const *second_slice, size_t i, u16_t const *scores_pre_substitution, u16_t const *scores_pre_insertion, u16_t const *scores_pre_deletion, u16_t const *scores_running_insertions, u16_t const *scores_running_deletions, u16_t *scores_new, u16_t *scores_new_insertions, u16_t *scores_new_deletions, u16_t gap_open, u16_t gap_extend) const noexcept {
u16_t const cost = first_reversed_slice[i] == second_slice[i] ? (u16_t)this->substituter_.match
: (u16_t)this->substituter_.mismatch;
u16_t const if_substitution = (u16_t)(scores_pre_substitution[i] + cost);
u16_t const if_insertion = sz_min_of_two((u16_t)(scores_pre_insertion[i] + gap_open),
(u16_t)(scores_running_insertions[i] + gap_extend));
u16_t const if_deletion = sz_min_of_two((u16_t)(scores_pre_deletion[i] + gap_open),
(u16_t)(scores_running_deletions[i] + gap_extend));
scores_new[i] = sz_min_of_two(sz_min_of_two(if_insertion, if_deletion), if_substitution);
scores_new_insertions[i] = if_insertion;
scores_new_deletions[i] = if_deletion;
}
SZ_NOINLINE void score_slice_trampoline_( u8_t const *first_reversed_slice, u8_t const *second_slice, u16_t const *scores_pre_substitution, u16_t const *scores_pre_insertion, u16_t const *scores_pre_deletion, u16_t const *scores_running_insertions, u16_t const *scores_running_deletions, u16_t *scores_new, u16_t *scores_new_insertions, u16_t *scores_new_deletions, __m256i match_cost_u8_vec, __m256i mismatch_cost_u8_vec, __m256i gap_open_vec, __m256i gap_extend_vec,
size_t from, size_t to) const noexcept {
for (size_t idx_slice = from; idx_slice < to; ++idx_slice) {
size_t const progress = idx_slice * step_k;
slice_32cells(first_reversed_slice + progress, second_slice + progress, scores_pre_substitution + progress,
scores_pre_insertion + progress, scores_pre_deletion + progress,
scores_running_insertions + progress, scores_running_deletions + progress,
scores_new + progress, scores_new_insertions + progress, scores_new_deletions + progress,
match_cost_u8_vec, mismatch_cost_u8_vec, gap_open_vec, gap_extend_vec);
}
}
template <typename executor_type_ = dummy_executor_t>
#if SZ_HAS_CONCEPTS_
requires executor_like<executor_type_>
#endif
void operator()( char const *first_reversed_slice, char const *second_slice, size_t const length, u16_t const *scores_pre_substitution, u16_t const *scores_pre_insertion, u16_t const *scores_pre_deletion, u16_t const *scores_running_insertions, u16_t const *scores_running_deletions, u16_t *scores_new, u16_t *scores_new_insertions, u16_t *scores_new_deletions, executor_type_ &&executor = {}) noexcept {
u8_t const *first_reversed = (u8_t const *)first_reversed_slice;
u8_t const *second = (u8_t const *)second_slice;
u16_t const gap_open = static_cast<u16_t>(this->gap_costs_.open);
u16_t const gap_extend = static_cast<u16_t>(this->gap_costs_.extend);
__m256i const match_cost_u8_vec = _mm256_set1_epi8((char)this->substituter_.match);
__m256i const mismatch_cost_u8_vec = _mm256_set1_epi8((char)this->substituter_.mismatch);
__m256i const gap_open_vec = _mm256_set1_epi16((short)gap_open);
__m256i const gap_extend_vec = _mm256_set1_epi16((short)gap_extend);
size_t const count_slices = length / step_k;
executor.for_slices(count_slices, [&](size_t from, size_t to) noexcept {
score_slice_trampoline_(first_reversed, second, scores_pre_substitution, scores_pre_insertion,
scores_pre_deletion, scores_running_insertions, scores_running_deletions,
scores_new, scores_new_insertions, scores_new_deletions, match_cost_u8_vec,
mismatch_cost_u8_vec, gap_open_vec, gap_extend_vec, from, to);
});
for (size_t i = count_slices * step_k; i < length; ++i)
slice_1cell(first_reversed, second, i, scores_pre_substitution, scores_pre_insertion, scores_pre_deletion,
scores_running_insertions, scores_running_deletions, scores_new, scores_new_insertions,
scores_new_deletions, gap_open, gap_extend);
this->last_score_ = scores_new[length - 1];
}
};
template <sz_capability_t capability_>
struct tile_scorer<char const *, char const *, u32_t, uniform_substitution_costs_t, affine_gap_costs_t,
sz_minimize_distance_k, sz_similarity_global_k, capability_,
std::enable_if_t<(capability_ & sz_cap_haswell_k) != 0>>
: public tile_scorer<char const *, char const *, u32_t, uniform_substitution_costs_t, affine_gap_costs_t,
sz_minimize_distance_k, sz_similarity_global_k, sz_cap_serial_k, void> {
using tile_scorer<char const *, char const *, u32_t, uniform_substitution_costs_t, affine_gap_costs_t,
sz_minimize_distance_k, sz_similarity_global_k, sz_cap_serial_k, void>::tile_scorer;
static constexpr sz_similarity_objective_t objective_k = sz_minimize_distance_k;
static constexpr sz_similarity_locality_t locality_k = sz_similarity_global_k;
static constexpr sz_capability_t capability_k = capability_;
static constexpr size_t step_k = 32;
SZ_INLINE void slice_32cells( u8_t const *first_reversed_slice, u8_t const *second_slice, u32_t const *scores_pre_substitution, u32_t const *scores_pre_insertion, u32_t const *scores_pre_deletion, u32_t const *scores_running_insertions, u32_t const *scores_running_deletions, u32_t *scores_new, u32_t *scores_new_insertions, u32_t *scores_new_deletions, __m256i match_cost_u8_vec, __m256i mismatch_cost_u8_vec, __m256i gap_open_vec, __m256i gap_extend_vec) const noexcept {
__m256i first_vec = _mm256_loadu_si256((__m256i const *)first_reversed_slice);
__m256i second_vec = _mm256_loadu_si256((__m256i const *)second_slice);
__m256i equal_vec = _mm256_cmpeq_epi8(first_vec, second_vec);
__m256i cost_u8_vec = _mm256_blendv_epi8(mismatch_cost_u8_vec, match_cost_u8_vec, equal_vec);
__m128i cost_low_xmm = _mm256_extracti128_si256(cost_u8_vec, 0);
__m128i cost_high_xmm = _mm256_extracti128_si256(cost_u8_vec, 1);
__m256i cost_u32_vecs[4];
cost_u32_vecs[0] = _mm256_cvtepu8_epi32(cost_low_xmm);
cost_u32_vecs[1] = _mm256_cvtepu8_epi32(_mm_srli_si128(cost_low_xmm, 8));
cost_u32_vecs[2] = _mm256_cvtepu8_epi32(cost_high_xmm);
cost_u32_vecs[3] = _mm256_cvtepu8_epi32(_mm_srli_si128(cost_high_xmm, 8));
for (size_t part = 0; part != 4; ++part) {
size_t const offset = part * 8;
__m256i pre_substitution_vec = _mm256_loadu_si256((__m256i const *)(scores_pre_substitution + offset));
__m256i pre_insert_open_vec = _mm256_loadu_si256((__m256i const *)(scores_pre_insertion + offset));
__m256i pre_delete_open_vec = _mm256_loadu_si256((__m256i const *)(scores_pre_deletion + offset));
__m256i run_insert_vec = _mm256_loadu_si256((__m256i const *)(scores_running_insertions + offset));
__m256i run_delete_vec = _mm256_loadu_si256((__m256i const *)(scores_running_deletions + offset));
__m256i cost_if_insert_vec = _mm256_min_epu32(_mm256_add_epi32(pre_insert_open_vec, gap_open_vec),
_mm256_add_epi32(run_insert_vec, gap_extend_vec));
__m256i cost_if_delete_vec = _mm256_min_epu32(_mm256_add_epi32(pre_delete_open_vec, gap_open_vec),
_mm256_add_epi32(run_delete_vec, gap_extend_vec));
__m256i cost_if_substitution_vec = _mm256_add_epi32(pre_substitution_vec, cost_u32_vecs[part]);
__m256i cell_score_vec = _mm256_min_epu32(_mm256_min_epu32(cost_if_insert_vec, cost_if_delete_vec),
cost_if_substitution_vec);
_mm256_storeu_si256((__m256i *)(scores_new + offset), cell_score_vec);
_mm256_storeu_si256((__m256i *)(scores_new_insertions + offset), cost_if_insert_vec);
_mm256_storeu_si256((__m256i *)(scores_new_deletions + offset), cost_if_delete_vec);
}
}
SZ_INLINE void slice_1cell( u8_t const *first_reversed_slice, u8_t const *second_slice, size_t i, u32_t const *scores_pre_substitution, u32_t const *scores_pre_insertion, u32_t const *scores_pre_deletion, u32_t const *scores_running_insertions, u32_t const *scores_running_deletions, u32_t *scores_new, u32_t *scores_new_insertions, u32_t *scores_new_deletions, u32_t gap_open, u32_t gap_extend) const noexcept {
u32_t const cost = first_reversed_slice[i] == second_slice[i] ? (u32_t)this->substituter_.match
: (u32_t)this->substituter_.mismatch;
u32_t const if_substitution = scores_pre_substitution[i] + cost;
u32_t const if_insertion = sz_min_of_two(scores_pre_insertion[i] + gap_open,
scores_running_insertions[i] + gap_extend);
u32_t const if_deletion = sz_min_of_two(scores_pre_deletion[i] + gap_open,
scores_running_deletions[i] + gap_extend);
scores_new[i] = sz_min_of_two(sz_min_of_two(if_insertion, if_deletion), if_substitution);
scores_new_insertions[i] = if_insertion;
scores_new_deletions[i] = if_deletion;
}
SZ_NOINLINE void score_slice_trampoline_( u8_t const *first_reversed_slice, u8_t const *second_slice, u32_t const *scores_pre_substitution, u32_t const *scores_pre_insertion, u32_t const *scores_pre_deletion, u32_t const *scores_running_insertions, u32_t const *scores_running_deletions, u32_t *scores_new, u32_t *scores_new_insertions, u32_t *scores_new_deletions, __m256i match_cost_u8_vec, __m256i mismatch_cost_u8_vec, __m256i gap_open_vec, __m256i gap_extend_vec,
size_t from, size_t to) const noexcept {
for (size_t idx_slice = from; idx_slice < to; ++idx_slice) {
size_t const progress = idx_slice * step_k;
slice_32cells(first_reversed_slice + progress, second_slice + progress, scores_pre_substitution + progress,
scores_pre_insertion + progress, scores_pre_deletion + progress,
scores_running_insertions + progress, scores_running_deletions + progress,
scores_new + progress, scores_new_insertions + progress, scores_new_deletions + progress,
match_cost_u8_vec, mismatch_cost_u8_vec, gap_open_vec, gap_extend_vec);
}
}
template <typename executor_type_ = dummy_executor_t>
#if SZ_HAS_CONCEPTS_
requires executor_like<executor_type_>
#endif
void operator()( char const *first_reversed_slice, char const *second_slice, size_t const length, u32_t const *scores_pre_substitution, u32_t const *scores_pre_insertion, u32_t const *scores_pre_deletion, u32_t const *scores_running_insertions, u32_t const *scores_running_deletions, u32_t *scores_new, u32_t *scores_new_insertions, u32_t *scores_new_deletions, executor_type_ &&executor = {}) noexcept {
u8_t const *first_reversed = (u8_t const *)first_reversed_slice;
u8_t const *second = (u8_t const *)second_slice;
u32_t const gap_open = static_cast<u32_t>(this->gap_costs_.open);
u32_t const gap_extend = static_cast<u32_t>(this->gap_costs_.extend);
__m256i const match_cost_u8_vec = _mm256_set1_epi8((char)this->substituter_.match);
__m256i const mismatch_cost_u8_vec = _mm256_set1_epi8((char)this->substituter_.mismatch);
__m256i const gap_open_vec = _mm256_set1_epi32((int)gap_open);
__m256i const gap_extend_vec = _mm256_set1_epi32((int)gap_extend);
size_t const count_slices = length / step_k;
executor.for_slices(count_slices, [&](size_t from, size_t to) noexcept {
score_slice_trampoline_(first_reversed, second, scores_pre_substitution, scores_pre_insertion,
scores_pre_deletion, scores_running_insertions, scores_running_deletions,
scores_new, scores_new_insertions, scores_new_deletions, match_cost_u8_vec,
mismatch_cost_u8_vec, gap_open_vec, gap_extend_vec, from, to);
});
for (size_t i = count_slices * step_k; i < length; ++i)
slice_1cell(first_reversed, second, i, scores_pre_substitution, scores_pre_insertion, scores_pre_deletion,
scores_running_insertions, scores_running_deletions, scores_new, scores_new_insertions,
scores_new_deletions, gap_open, gap_extend);
this->last_score_ = scores_new[length - 1];
}
};
template <sz_capability_t capability_>
struct tile_scorer<char const *, char const *, u8_t, uniform_substitution_costs_t, linear_gap_costs_t,
sz_minimize_distance_k, sz_similarity_global_k, capability_,
std::enable_if_t<(capability_ & sz_cap_haswell_k) != 0>>
: public tile_scorer<char const *, char const *, u8_t, uniform_substitution_costs_t, linear_gap_costs_t,
sz_minimize_distance_k, sz_similarity_global_k, sz_cap_serial_k, void> {
using tile_scorer<char const *, char const *, u8_t, uniform_substitution_costs_t, linear_gap_costs_t,
sz_minimize_distance_k, sz_similarity_global_k, sz_cap_serial_k, void>::tile_scorer;
static constexpr sz_similarity_objective_t objective_k = sz_minimize_distance_k;
static constexpr sz_similarity_locality_t locality_k = sz_similarity_global_k;
static constexpr sz_capability_t capability_k = capability_;
static constexpr size_t step_k = 32;
SZ_INLINE void slice_32cells( u8_t const *first_reversed_slice, u8_t const *second_slice, u8_t const *scores_pre_substitution, u8_t const *scores_pre_insertion, u8_t const *scores_pre_deletion, u8_t *scores_new, __m256i match_cost_vec, __m256i mismatch_cost_vec, __m256i gap_cost_vec) const noexcept {
__m256i first_vec = _mm256_loadu_si256((__m256i const *)first_reversed_slice);
__m256i second_vec = _mm256_loadu_si256((__m256i const *)second_slice);
__m256i equal_vec = _mm256_cmpeq_epi8(first_vec, second_vec);
__m256i cost_vec = _mm256_blendv_epi8(mismatch_cost_vec, match_cost_vec, equal_vec);
__m256i pre_substitution_vec = _mm256_loadu_si256((__m256i const *)scores_pre_substitution);
__m256i pre_insert_vec = _mm256_loadu_si256((__m256i const *)scores_pre_insertion);
__m256i pre_delete_vec = _mm256_loadu_si256((__m256i const *)scores_pre_deletion);
__m256i cost_if_substitution_vec = _mm256_adds_epu8(pre_substitution_vec, cost_vec);
__m256i cost_if_gap_vec = _mm256_adds_epu8(_mm256_min_epu8(pre_insert_vec, pre_delete_vec), gap_cost_vec);
_mm256_storeu_si256((__m256i *)scores_new, _mm256_min_epu8(cost_if_substitution_vec, cost_if_gap_vec));
}
SZ_INLINE void slice_1cell( u8_t const *first_reversed_slice, u8_t const *second_slice, size_t i, u8_t const *scores_pre_substitution, u8_t const *scores_pre_insertion, u8_t const *scores_pre_deletion, u8_t *scores_new, u8_t gap) const noexcept {
u8_t const cost = first_reversed_slice[i] == second_slice[i] ? (u8_t)this->substituter_.match
: (u8_t)this->substituter_.mismatch;
u8_t const if_substitution = (u8_t)(scores_pre_substitution[i] + cost);
u8_t const if_gap = (u8_t)(sz_min_of_two(scores_pre_insertion[i], scores_pre_deletion[i]) + gap);
scores_new[i] = sz_min_of_two(if_substitution, if_gap);
}
SZ_NOINLINE void score_slice_trampoline_( u8_t const *first_reversed_slice, u8_t const *second_slice, u8_t const *scores_pre_substitution, u8_t const *scores_pre_insertion, u8_t const *scores_pre_deletion, u8_t *scores_new, __m256i match_cost_vec, __m256i mismatch_cost_vec, __m256i gap_cost_vec, size_t from, size_t to) const noexcept {
for (size_t idx_slice = from; idx_slice < to; ++idx_slice) {
size_t const progress = idx_slice * step_k;
slice_32cells(first_reversed_slice + progress, second_slice + progress, scores_pre_substitution + progress,
scores_pre_insertion + progress, scores_pre_deletion + progress, scores_new + progress,
match_cost_vec, mismatch_cost_vec, gap_cost_vec);
}
}
template <typename executor_type_ = dummy_executor_t>
#if SZ_HAS_CONCEPTS_
requires executor_like<executor_type_>
#endif
void operator()( char const *first_reversed_slice, char const *second_slice, size_t const length, u8_t const *scores_pre_substitution, u8_t const *scores_pre_insertion, u8_t const *scores_pre_deletion, u8_t *scores_new, executor_type_ &&executor = {}) noexcept {
u8_t const *first_reversed = (u8_t const *)first_reversed_slice;
u8_t const *second = (u8_t const *)second_slice;
u8_t const gap = static_cast<u8_t>(this->gap_costs_.open_or_extend);
__m256i const match_cost_vec = _mm256_set1_epi8((char)this->substituter_.match);
__m256i const mismatch_cost_vec = _mm256_set1_epi8((char)this->substituter_.mismatch);
__m256i const gap_cost_vec = _mm256_set1_epi8((char)gap);
size_t const count_slices = length / step_k;
executor.for_slices(count_slices, [&](size_t from, size_t to) noexcept {
score_slice_trampoline_(first_reversed, second, scores_pre_substitution, scores_pre_insertion,
scores_pre_deletion, scores_new, match_cost_vec, mismatch_cost_vec, gap_cost_vec,
from, to);
});
for (size_t i = count_slices * step_k; i < length; ++i)
slice_1cell(first_reversed, second, i, scores_pre_substitution, scores_pre_insertion, scores_pre_deletion,
scores_new, gap);
this->last_score_ = scores_new[length - 1];
}
};
template <sz_capability_t capability_>
struct tile_scorer<char const *, char const *, u8_t, uniform_substitution_costs_t, affine_gap_costs_t,
sz_minimize_distance_k, sz_similarity_global_k, capability_,
std::enable_if_t<(capability_ & sz_cap_haswell_k) != 0>>
: public tile_scorer<char const *, char const *, u8_t, uniform_substitution_costs_t, affine_gap_costs_t,
sz_minimize_distance_k, sz_similarity_global_k, sz_cap_serial_k, void> {
using tile_scorer<char const *, char const *, u8_t, uniform_substitution_costs_t, affine_gap_costs_t,
sz_minimize_distance_k, sz_similarity_global_k, sz_cap_serial_k, void>::tile_scorer;
static constexpr sz_similarity_objective_t objective_k = sz_minimize_distance_k;
static constexpr sz_similarity_locality_t locality_k = sz_similarity_global_k;
static constexpr sz_capability_t capability_k = capability_;
static constexpr size_t step_k = 32;
SZ_INLINE void slice_32cells( u8_t const *first_reversed_slice, u8_t const *second_slice, u8_t const *scores_pre_substitution, u8_t const *scores_pre_insertion, u8_t const *scores_pre_deletion, u8_t const *scores_running_insertions, u8_t const *scores_running_deletions, u8_t *scores_new, u8_t *scores_new_insertions, u8_t *scores_new_deletions, __m256i match_cost_vec, __m256i mismatch_cost_vec, __m256i gap_open_vec,
__m256i gap_extend_vec) const noexcept {
__m256i first_vec = _mm256_loadu_si256((__m256i const *)first_reversed_slice);
__m256i second_vec = _mm256_loadu_si256((__m256i const *)second_slice);
__m256i equal_vec = _mm256_cmpeq_epi8(first_vec, second_vec);
__m256i cost_vec = _mm256_blendv_epi8(mismatch_cost_vec, match_cost_vec, equal_vec);
__m256i pre_insert_open_vec = _mm256_loadu_si256((__m256i const *)scores_pre_insertion);
__m256i pre_delete_open_vec = _mm256_loadu_si256((__m256i const *)scores_pre_deletion);
__m256i run_insert_vec = _mm256_loadu_si256((__m256i const *)scores_running_insertions);
__m256i run_delete_vec = _mm256_loadu_si256((__m256i const *)scores_running_deletions);
__m256i cost_if_insert_vec = _mm256_min_epu8(_mm256_adds_epu8(pre_insert_open_vec, gap_open_vec),
_mm256_adds_epu8(run_insert_vec, gap_extend_vec));
__m256i cost_if_delete_vec = _mm256_min_epu8(_mm256_adds_epu8(pre_delete_open_vec, gap_open_vec),
_mm256_adds_epu8(run_delete_vec, gap_extend_vec));
__m256i cost_if_substitution_vec = _mm256_adds_epu8(
_mm256_loadu_si256((__m256i const *)scores_pre_substitution), cost_vec);
__m256i cell_score_vec = _mm256_min_epu8(_mm256_min_epu8(cost_if_insert_vec, cost_if_delete_vec),
cost_if_substitution_vec);
_mm256_storeu_si256((__m256i *)scores_new, cell_score_vec);
_mm256_storeu_si256((__m256i *)scores_new_insertions, cost_if_insert_vec);
_mm256_storeu_si256((__m256i *)scores_new_deletions, cost_if_delete_vec);
}
SZ_INLINE void slice_1cell( u8_t const *first_reversed_slice, u8_t const *second_slice, size_t i, u8_t const *scores_pre_substitution, u8_t const *scores_pre_insertion, u8_t const *scores_pre_deletion, u8_t const *scores_running_insertions, u8_t const *scores_running_deletions, u8_t *scores_new, u8_t *scores_new_insertions, u8_t *scores_new_deletions, u8_t gap_open, u8_t gap_extend) const noexcept {
u8_t const cost = first_reversed_slice[i] == second_slice[i] ? (u8_t)this->substituter_.match
: (u8_t)this->substituter_.mismatch;
u8_t const if_substitution = (u8_t)(scores_pre_substitution[i] + cost);
u8_t const if_insertion = sz_min_of_two((u8_t)(scores_pre_insertion[i] + gap_open),
(u8_t)(scores_running_insertions[i] + gap_extend));
u8_t const if_deletion = sz_min_of_two((u8_t)(scores_pre_deletion[i] + gap_open),
(u8_t)(scores_running_deletions[i] + gap_extend));
scores_new[i] = sz_min_of_two(sz_min_of_two(if_insertion, if_deletion), if_substitution);
scores_new_insertions[i] = if_insertion;
scores_new_deletions[i] = if_deletion;
}
SZ_NOINLINE void score_slice_trampoline_( u8_t const *first_reversed_slice, u8_t const *second_slice, u8_t const *scores_pre_substitution, u8_t const *scores_pre_insertion, u8_t const *scores_pre_deletion, u8_t const *scores_running_insertions, u8_t const *scores_running_deletions, u8_t *scores_new, u8_t *scores_new_insertions, u8_t *scores_new_deletions, __m256i match_cost_vec, __m256i mismatch_cost_vec, __m256i gap_open_vec, __m256i gap_extend_vec, size_t from,
size_t to) const noexcept {
for (size_t idx_slice = from; idx_slice < to; ++idx_slice) {
size_t const progress = idx_slice * step_k;
slice_32cells(first_reversed_slice + progress, second_slice + progress, scores_pre_substitution + progress,
scores_pre_insertion + progress, scores_pre_deletion + progress,
scores_running_insertions + progress, scores_running_deletions + progress,
scores_new + progress, scores_new_insertions + progress, scores_new_deletions + progress,
match_cost_vec, mismatch_cost_vec, gap_open_vec, gap_extend_vec);
}
}
template <typename executor_type_ = dummy_executor_t>
#if SZ_HAS_CONCEPTS_
requires executor_like<executor_type_>
#endif
void operator()( char const *first_reversed_slice, char const *second_slice, size_t const length, u8_t const *scores_pre_substitution, u8_t const *scores_pre_insertion, u8_t const *scores_pre_deletion, u8_t const *scores_running_insertions, u8_t const *scores_running_deletions, u8_t *scores_new, u8_t *scores_new_insertions, u8_t *scores_new_deletions, executor_type_ &&executor = {}) noexcept {
u8_t const *first_reversed = (u8_t const *)first_reversed_slice;
u8_t const *second = (u8_t const *)second_slice;
u8_t const gap_open = static_cast<u8_t>(this->gap_costs_.open);
u8_t const gap_extend = static_cast<u8_t>(this->gap_costs_.extend);
__m256i const match_cost_vec = _mm256_set1_epi8((char)this->substituter_.match);
__m256i const mismatch_cost_vec = _mm256_set1_epi8((char)this->substituter_.mismatch);
__m256i const gap_open_vec = _mm256_set1_epi8((char)gap_open);
__m256i const gap_extend_vec = _mm256_set1_epi8((char)gap_extend);
size_t const count_slices = length / step_k;
executor.for_slices(count_slices, [&](size_t from, size_t to) noexcept {
score_slice_trampoline_(first_reversed, second, scores_pre_substitution, scores_pre_insertion,
scores_pre_deletion, scores_running_insertions, scores_running_deletions,
scores_new, scores_new_insertions, scores_new_deletions, match_cost_vec,
mismatch_cost_vec, gap_open_vec, gap_extend_vec, from, to);
});
for (size_t i = count_slices * step_k; i < length; ++i)
slice_1cell(first_reversed, second, i, scores_pre_substitution, scores_pre_insertion, scores_pre_deletion,
scores_running_insertions, scores_running_deletions, scores_new, scores_new_insertions,
scores_new_deletions, gap_open, gap_extend);
this->last_score_ = scores_new[length - 1];
}
};
template <sz_capability_t capability_>
struct tile_scorer<rune_t const *, rune_t const *, u16_t, uniform_substitution_costs_t, linear_gap_costs_t,
sz_minimize_distance_k, sz_similarity_global_k, capability_,
std::enable_if_t<(capability_ & sz_cap_haswell_k) != 0>>
: public tile_scorer<rune_t const *, rune_t const *, u16_t, uniform_substitution_costs_t, linear_gap_costs_t,
sz_minimize_distance_k, sz_similarity_global_k, sz_cap_serial_k, void> {
using tile_scorer<rune_t const *, rune_t const *, u16_t, uniform_substitution_costs_t, linear_gap_costs_t,
sz_minimize_distance_k, sz_similarity_global_k, sz_cap_serial_k, void>::tile_scorer;
static constexpr sz_similarity_objective_t objective_k = sz_minimize_distance_k;
static constexpr sz_similarity_locality_t locality_k = sz_similarity_global_k;
static constexpr sz_capability_t capability_k = capability_;
static constexpr size_t step_k = 32;
SZ_INLINE void slice_32cells( rune_t const *first_reversed_slice, rune_t const *second_slice, u16_t const *scores_pre_substitution, u16_t const *scores_pre_insertion, u16_t const *scores_pre_deletion, u16_t *scores_new, __m256i match_cost_vec, __m256i mismatch_cost_vec, __m256i gap_cost_vec) const noexcept {
u32_t const *first = (u32_t const *)first_reversed_slice;
u32_t const *second = (u32_t const *)second_slice;
for (size_t part = 0; part != 2; ++part) {
u32_t const *first_part = first + part * 16;
u32_t const *second_part = second + part * 16;
__m256i equal_low_vec = _mm256_cmpeq_epi32(_mm256_loadu_si256((__m256i const *)(first_part + 0)),
_mm256_loadu_si256((__m256i const *)(second_part + 0)));
__m256i equal_high_vec = _mm256_cmpeq_epi32(_mm256_loadu_si256((__m256i const *)(first_part + 8)),
_mm256_loadu_si256((__m256i const *)(second_part + 8)));
__m256i equal_packed_vec = _mm256_packs_epi32(equal_low_vec, equal_high_vec);
__m256i equal_i16_vec = _mm256_permute4x64_epi64(equal_packed_vec, 0xD8);
__m256i cost_vec = _mm256_blendv_epi8(mismatch_cost_vec, match_cost_vec, equal_i16_vec);
__m256i pre_substitution_vec = _mm256_loadu_si256((__m256i const *)(scores_pre_substitution + part * 16));
__m256i pre_insert_vec = _mm256_loadu_si256((__m256i const *)(scores_pre_insertion + part * 16));
__m256i pre_delete_vec = _mm256_loadu_si256((__m256i const *)(scores_pre_deletion + part * 16));
__m256i cost_if_substitution_vec = _mm256_add_epi16(pre_substitution_vec, cost_vec);
__m256i cost_if_gap_vec = _mm256_add_epi16(_mm256_min_epu16(pre_insert_vec, pre_delete_vec), gap_cost_vec);
__m256i cell_score_vec = _mm256_min_epu16(cost_if_substitution_vec, cost_if_gap_vec);
_mm256_storeu_si256((__m256i *)(scores_new + part * 16), cell_score_vec);
}
}
SZ_INLINE void slice_1cell( rune_t const *first_reversed_slice, rune_t const *second_slice, size_t i, u16_t const *scores_pre_substitution, u16_t const *scores_pre_insertion, u16_t const *scores_pre_deletion, u16_t *scores_new, u16_t gap) const noexcept {
u16_t const cost = first_reversed_slice[i] == second_slice[i] ? (u16_t)this->substituter_.match
: (u16_t)this->substituter_.mismatch;
u16_t const if_substitution = (u16_t)(scores_pre_substitution[i] + cost);
u16_t const if_gap = (u16_t)(sz_min_of_two(scores_pre_insertion[i], scores_pre_deletion[i]) + gap);
scores_new[i] = sz_min_of_two(if_substitution, if_gap);
}
SZ_NOINLINE void score_slice_trampoline_( rune_t const *first_reversed_slice, rune_t const *second_slice, u16_t const *scores_pre_substitution, u16_t const *scores_pre_insertion, u16_t const *scores_pre_deletion, u16_t *scores_new, __m256i match_cost_vec, __m256i mismatch_cost_vec, __m256i gap_cost_vec, size_t from, size_t to) const noexcept {
for (size_t idx_slice = from; idx_slice < to; ++idx_slice) {
size_t const progress = idx_slice * step_k;
slice_32cells(first_reversed_slice + progress, second_slice + progress, scores_pre_substitution + progress,
scores_pre_insertion + progress, scores_pre_deletion + progress, scores_new + progress,
match_cost_vec, mismatch_cost_vec, gap_cost_vec);
}
}
template <typename executor_type_ = dummy_executor_t>
#if SZ_HAS_CONCEPTS_
requires executor_like<executor_type_>
#endif
void operator()( rune_t const *first_reversed_slice, rune_t const *second_slice, size_t const length, u16_t const *scores_pre_substitution, u16_t const *scores_pre_insertion, u16_t const *scores_pre_deletion, u16_t *scores_new, executor_type_ &&executor = {}) noexcept {
u16_t const gap = static_cast<u16_t>(this->gap_costs_.open_or_extend);
__m256i const match_cost_vec = _mm256_set1_epi16((short)this->substituter_.match);
__m256i const mismatch_cost_vec = _mm256_set1_epi16((short)this->substituter_.mismatch);
__m256i const gap_cost_vec = _mm256_set1_epi16((short)gap);
size_t const count_slices = length / step_k;
executor.for_slices(count_slices, [&](size_t from, size_t to) noexcept {
score_slice_trampoline_(first_reversed_slice, second_slice, scores_pre_substitution, scores_pre_insertion,
scores_pre_deletion, scores_new, match_cost_vec, mismatch_cost_vec, gap_cost_vec,
from, to);
});
for (size_t i = count_slices * step_k; i < length; ++i)
slice_1cell(first_reversed_slice, second_slice, i, scores_pre_substitution, scores_pre_insertion,
scores_pre_deletion, scores_new, gap);
this->last_score_ = scores_new[length - 1];
}
};
template <sz_capability_t capability_>
struct tile_scorer<rune_t const *, rune_t const *, u8_t, uniform_substitution_costs_t, linear_gap_costs_t,
sz_minimize_distance_k, sz_similarity_global_k, capability_,
std::enable_if_t<(capability_ & sz_cap_haswell_k) != 0>>
: public tile_scorer<rune_t const *, rune_t const *, u8_t, uniform_substitution_costs_t, linear_gap_costs_t,
sz_minimize_distance_k, sz_similarity_global_k, sz_cap_serial_k, void> {
using tile_scorer<rune_t const *, rune_t const *, u8_t, uniform_substitution_costs_t, linear_gap_costs_t,
sz_minimize_distance_k, sz_similarity_global_k, sz_cap_serial_k, void>::tile_scorer;
static constexpr sz_similarity_objective_t objective_k = sz_minimize_distance_k;
static constexpr sz_similarity_locality_t locality_k = sz_similarity_global_k;
static constexpr sz_capability_t capability_k = capability_;
static constexpr size_t step_k = 32;
SZ_INLINE void slice_32cells( rune_t const *first_reversed_slice, rune_t const *second_slice, u8_t const *scores_pre_substitution, u8_t const *scores_pre_insertion, u8_t const *scores_pre_deletion, u8_t *scores_new, __m256i match_cost_vec, __m256i mismatch_cost_vec, __m256i gap_cost_vec) const noexcept {
u32_t const *first = (u32_t const *)first_reversed_slice;
u32_t const *second = (u32_t const *)second_slice;
__m256i equal_0 = _mm256_cmpeq_epi32(_mm256_loadu_si256((__m256i const *)(first + 0)),
_mm256_loadu_si256((__m256i const *)(second + 0)));
__m256i equal_1 = _mm256_cmpeq_epi32(_mm256_loadu_si256((__m256i const *)(first + 8)),
_mm256_loadu_si256((__m256i const *)(second + 8)));
__m256i equal_2 = _mm256_cmpeq_epi32(_mm256_loadu_si256((__m256i const *)(first + 16)),
_mm256_loadu_si256((__m256i const *)(second + 16)));
__m256i equal_3 = _mm256_cmpeq_epi32(_mm256_loadu_si256((__m256i const *)(first + 24)),
_mm256_loadu_si256((__m256i const *)(second + 24)));
__m256i equal_low_i16 = _mm256_permute4x64_epi64(_mm256_packs_epi32(equal_0, equal_1), 0xD8);
__m256i equal_high_i16 = _mm256_permute4x64_epi64(_mm256_packs_epi32(equal_2, equal_3), 0xD8);
__m256i equal_i8_vec = _mm256_permute4x64_epi64(_mm256_packs_epi16(equal_low_i16, equal_high_i16), 0xD8);
__m256i cost_vec = _mm256_blendv_epi8(mismatch_cost_vec, match_cost_vec, equal_i8_vec);
__m256i pre_substitution_vec = _mm256_loadu_si256((__m256i const *)scores_pre_substitution);
__m256i pre_insert_vec = _mm256_loadu_si256((__m256i const *)scores_pre_insertion);
__m256i pre_delete_vec = _mm256_loadu_si256((__m256i const *)scores_pre_deletion);
__m256i cost_if_substitution_vec = _mm256_adds_epu8(pre_substitution_vec, cost_vec);
__m256i cost_if_gap_vec = _mm256_adds_epu8(_mm256_min_epu8(pre_insert_vec, pre_delete_vec), gap_cost_vec);
_mm256_storeu_si256((__m256i *)scores_new, _mm256_min_epu8(cost_if_substitution_vec, cost_if_gap_vec));
}
SZ_INLINE void slice_1cell( rune_t const *first_reversed_slice, rune_t const *second_slice, size_t i, u8_t const *scores_pre_substitution, u8_t const *scores_pre_insertion, u8_t const *scores_pre_deletion, u8_t *scores_new, u8_t gap) const noexcept {
u8_t const cost = first_reversed_slice[i] == second_slice[i] ? (u8_t)this->substituter_.match
: (u8_t)this->substituter_.mismatch;
u8_t const if_substitution = (u8_t)(scores_pre_substitution[i] + cost);
u8_t const if_gap = (u8_t)(sz_min_of_two(scores_pre_insertion[i], scores_pre_deletion[i]) + gap);
scores_new[i] = sz_min_of_two(if_substitution, if_gap);
}
SZ_NOINLINE void score_slice_trampoline_( rune_t const *first_reversed_slice, rune_t const *second_slice, u8_t const *scores_pre_substitution, u8_t const *scores_pre_insertion, u8_t const *scores_pre_deletion, u8_t *scores_new, __m256i match_cost_vec, __m256i mismatch_cost_vec, __m256i gap_cost_vec, size_t from, size_t to) const noexcept {
for (size_t idx_slice = from; idx_slice < to; ++idx_slice) {
size_t const progress = idx_slice * step_k;
slice_32cells(first_reversed_slice + progress, second_slice + progress, scores_pre_substitution + progress,
scores_pre_insertion + progress, scores_pre_deletion + progress, scores_new + progress,
match_cost_vec, mismatch_cost_vec, gap_cost_vec);
}
}
template <typename executor_type_ = dummy_executor_t>
#if SZ_HAS_CONCEPTS_
requires executor_like<executor_type_>
#endif
void operator()( rune_t const *first_reversed_slice, rune_t const *second_slice, size_t const length, u8_t const *scores_pre_substitution, u8_t const *scores_pre_insertion, u8_t const *scores_pre_deletion, u8_t *scores_new, executor_type_ &&executor = {}) noexcept {
u8_t const gap = static_cast<u8_t>(this->gap_costs_.open_or_extend);
__m256i const match_cost_vec = _mm256_set1_epi8((char)this->substituter_.match);
__m256i const mismatch_cost_vec = _mm256_set1_epi8((char)this->substituter_.mismatch);
__m256i const gap_cost_vec = _mm256_set1_epi8((char)gap);
size_t const count_slices = length / step_k;
executor.for_slices(count_slices, [&](size_t from, size_t to) noexcept {
score_slice_trampoline_(first_reversed_slice, second_slice, scores_pre_substitution, scores_pre_insertion,
scores_pre_deletion, scores_new, match_cost_vec, mismatch_cost_vec, gap_cost_vec,
from, to);
});
for (size_t i = count_slices * step_k; i < length; ++i)
slice_1cell(first_reversed_slice, second_slice, i, scores_pre_substitution, scores_pre_insertion,
scores_pre_deletion, scores_new, gap);
this->last_score_ = scores_new[length - 1];
}
};
#pragma endregion
template <typename char_type_, typename score_type_, typename substituter_type_, typename gap_costs_type_,
sz_similarity_objective_t objective_, sz_similarity_locality_t locality_>
struct horizontal_walker<char_type_, score_type_, substituter_type_, gap_costs_type_, objective_, locality_,
sz_cap_haswell_k, void>
: public horizontal_walker<char_type_, score_type_, substituter_type_, gap_costs_type_, objective_, locality_,
sz_cap_serial_k, void> {
using base_t = horizontal_walker<char_type_, score_type_, substituter_type_, gap_costs_type_, objective_, locality_,
sz_cap_serial_k, void>;
using base_t::base_t;
using base_t::operator();
};
template <typename char_type_, typename score_type_, sz_similarity_objective_t objective_,
sz_similarity_locality_t locality_>
struct horizontal_walker<char_type_, score_type_, error_costs_32x32_t, linear_gap_costs_t, objective_, locality_,
sz_cap_haswell_k, void> {
using char_t = char_type_;
using score_t = score_type_;
using substituter_t = error_costs_32x32_t;
using gap_costs_t = linear_gap_costs_t;
static constexpr sz_similarity_objective_t objective_k = objective_;
static constexpr sz_similarity_locality_t locality_k = locality_;
static constexpr sz_capability_t capability_k = sz_cap_haswell_k;
using tile_scorer_t = tile_scorer<constant_iterator<char_t>, char_t const *, score_t, substituter_t, gap_costs_t,
objective_k, locality_k, capability_k>;
substituter_t substituter_ {};
linear_gap_costs_t gap_costs_ {};
horizontal_walker() noexcept {}
horizontal_walker(substituter_t subs, linear_gap_costs_t gaps) noexcept : substituter_(subs), gap_costs_(gaps) {}
template <typename executor_type_>
#if SZ_HAS_CONCEPTS_
requires executor_like<executor_type_>
#endif
status_t operator()(span<char_t const> const &first, span<char_t const> const &second, score_t &result_ref,
scratch_space_t scratch_space, executor_type_ &&executor,
cpu_specs_t const &specs) const noexcept {
if (first.empty() || second.empty()) {
result_ref = 0;
if constexpr (locality_k == sz_similarity_global_k) {
if (!first.empty() && second.empty()) { result_ref = gap_costs_.open_or_extend * first.size(); }
else if (first.empty() && !second.empty()) { result_ref = gap_costs_.open_or_extend * second.size(); }
}
return status_t::success_k;
}
char_t const *shorter = first.data(), *longer = second.data();
size_t shorter_length = first.size(), longer_length = second.size();
if (shorter_length > longer_length) {
trivial_swap(shorter, longer);
trivial_swap(shorter_length, longer_length);
}
size_t const shorter_dim = shorter_length + 1;
size_t const longer_dim = longer_length + 1;
size_t const padded_shorter_dim = round_up_to_multiple(sizeof(score_t) * shorter_dim, specs.cache_line_width) /
sizeof(score_t);
size_t const scratch_required = sizeof(score_t) * padded_shorter_dim * 2;
if (scratch_space.size() < scratch_required) return status_t::bad_alloc_k;
score_t *previous_scores = (score_t *)scratch_space.data();
score_t *current_scores = previous_scores + padded_shorter_dim;
tile_scorer_t scorer {substituter_, gap_costs_};
for (size_t col_idx = 0; col_idx < shorter_dim; ++col_idx) scorer.init_score(previous_scores[col_idx], col_idx);
for (size_t row_idx = 1; row_idx < longer_dim; ++row_idx) {
scorer.init_score(current_scores[0], row_idx);
scorer( constant_iterator<char_t> {longer[row_idx - 1]}, shorter, shorter_dim - 1, previous_scores, previous_scores + 1, current_scores, current_scores + 1, executor );
trivial_swap(previous_scores, current_scores);
}
result_ref = scorer.score();
return status_t::success_k;
}
};
template <typename score_type_, sz_similarity_objective_t objective_, sz_similarity_locality_t locality_>
struct diagonal_walker<char, score_type_, error_costs_32x32_t, linear_gap_costs_t, objective_, locality_,
sz_cap_haswell_k, void> {
using char_t = char;
using score_t = score_type_;
using substituter_t = error_costs_32x32_t;
using gap_costs_t = linear_gap_costs_t;
static constexpr sz_similarity_objective_t objective_k = objective_;
static constexpr sz_similarity_locality_t locality_k = locality_;
static constexpr sz_capability_t capability_k = sz_cap_haswell_k;
using tile_scorer_t = tile_scorer<char_t const *, char_t const *, score_t, substituter_t, gap_costs_t, objective_k,
locality_k, capability_k>;
substituter_t substituter_ {};
linear_gap_costs_t gap_costs_ {};
diagonal_walker() noexcept {}
diagonal_walker(substituter_t subs, linear_gap_costs_t gaps) noexcept : substituter_(subs), gap_costs_(gaps) {}
struct layout_t {
size_t previous_scores = 0; size_t current_scores = 0;
size_t next_scores = 0;
size_t shorter_reversed = 0; size_t shorter_reversed_classes = 0; size_t longer_classes = 0; size_t total = 0; constexpr operator size_t() const noexcept { return total; }
};
layout_t layout(span<char_t const> first, span<char_t const> second, cpu_specs_t const &specs) const noexcept {
size_t const shorter_length = sz_min_of_two(first.size(), second.size());
size_t const longer_length = sz_max_of_two(first.size(), second.size());
size_t const diagonal_bytes = sizeof(score_t) * (shorter_length + 1); size_t const shorter_stream_bytes = shorter_length + step_classes_k; size_t const longer_stream_bytes = longer_length + step_classes_k;
scratch_amount_t amount {specs.cache_line_width};
layout_t at;
at.previous_scores = amount, amount += diagonal_bytes;
at.current_scores = amount, amount += diagonal_bytes;
at.next_scores = amount, amount += diagonal_bytes;
at.shorter_reversed = amount, amount += shorter_stream_bytes;
at.shorter_reversed_classes = amount, amount += shorter_stream_bytes;
at.longer_classes = amount, amount += longer_stream_bytes;
at.total = amount;
return at;
}
template <typename executor_type_>
#if SZ_HAS_CONCEPTS_
requires executor_like<executor_type_>
#endif
status_t operator()(span<char_t const> const &first, span<char_t const> const &second, score_t &result_ref,
scratch_space_t scratch_space, executor_type_ &&executor,
cpu_specs_t const &specs) const noexcept {
if (first.empty() || second.empty()) {
result_ref = 0;
if constexpr (locality_k == sz_similarity_global_k) {
if (!first.empty() && second.empty()) { result_ref = gap_costs_.open_or_extend * first.size(); }
else if (first.empty() && !second.empty()) { result_ref = gap_costs_.open_or_extend * second.size(); }
}
return status_t::success_k;
}
char_t const *shorter = first.data(), *longer = second.data();
size_t shorter_length = first.size(), longer_length = second.size();
bool transpose = false;
if (shorter_length > longer_length) {
trivial_swap(shorter, longer);
trivial_swap(shorter_length, longer_length);
transpose = true;
}
size_t const shorter_dim = shorter_length + 1;
size_t const longer_dim = longer_length + 1;
size_t const diagonals_count = shorter_dim + longer_dim - 1;
size_t const max_diagonal_length = shorter_length + 1;
layout_t const at = layout(first, second, specs);
if (scratch_space.size() < at.total) return status_t::bad_alloc_k;
score_t *previous_scores = (score_t *)(scratch_space.data() + at.previous_scores);
score_t *current_scores = (score_t *)(scratch_space.data() + at.current_scores);
score_t *next_scores = (score_t *)(scratch_space.data() + at.next_scores);
char_t *const shorter_reversed = (char_t *)(scratch_space.data() + at.shorter_reversed);
char_t *const shorter_reversed_classes = (char_t *)(scratch_space.data() + at.shorter_reversed_classes);
char_t *const longer_classes = (char_t *)(scratch_space.data() + at.longer_classes);
for (size_t i = 0; i != shorter_length; ++i) shorter_reversed[i] = shorter[shorter_length - 1 - i];
tile_scorer_t scorer {substituter_, gap_costs_};
scorer.lookup_.reload_classes(substituter_.byte_to_class);
scorer.prepare(transpose);
classify_into_(scorer.lookup_, shorter_reversed, shorter_length, shorter_reversed_classes);
classify_into_(scorer.lookup_, longer, longer_length, longer_classes);
scorer.init_score(previous_scores[0], 0);
scorer.init_score(current_scores[0], 1);
scorer.init_score(current_scores[1], 1);
size_t next_diagonal_index = 2;
for (; next_diagonal_index < shorter_dim; ++next_diagonal_index) {
size_t const next_diagonal_length = next_diagonal_index + 1;
scorer( shorter_reversed_classes + shorter_length - next_diagonal_index + 1, longer_classes, next_diagonal_length - 2, previous_scores, current_scores, current_scores + 1, next_scores + 1, executor);
scorer.init_score(next_scores[0], next_diagonal_index);
scorer.init_score(next_scores[next_diagonal_length - 1], next_diagonal_index);
rotate_three(previous_scores, current_scores, next_scores);
}
for (; next_diagonal_index < longer_dim; ++next_diagonal_index) {
size_t const next_diagonal_length = shorter_dim;
scorer( shorter_reversed_classes + shorter_length - shorter_dim + 1, longer_classes + next_diagonal_index - shorter_dim, next_diagonal_length - 1, previous_scores, current_scores, current_scores + 1, next_scores, executor);
scorer.init_score(next_scores[next_diagonal_length - 1], next_diagonal_index);
rotate_three(previous_scores, current_scores, next_scores);
sz_move_serial((ptr_t)(previous_scores), (ptr_t)(previous_scores + 1),
(max_diagonal_length - 1) * sizeof(score_t));
}
for (; next_diagonal_index < diagonals_count; ++next_diagonal_index) {
size_t const next_diagonal_length = diagonals_count - next_diagonal_index;
scorer( shorter_reversed_classes + shorter_length - shorter_dim + 1, longer_classes + next_diagonal_index - shorter_dim, next_diagonal_length, previous_scores, current_scores, current_scores + 1, next_scores, executor);
rotate_three(previous_scores, current_scores, next_scores);
previous_scores++;
}
result_ref = scorer.score();
return status_t::success_k;
}
static constexpr size_t step_classes_k = 32;
private:
static void classify_into_(substitution_lookup_haswell_t const &lookup, char_t const *source, size_t length,
char_t *classes) noexcept {
u256_vec_t source_vec, classes_vec;
size_t progress = 0;
for (; progress + step_classes_k <= length; progress += step_classes_k) {
source_vec.ymm = _mm256_loadu_si256((__m256i const *)(source + progress));
classes_vec = lookup.classify32(source_vec);
_mm256_storeu_si256((__m256i *)(classes + progress), classes_vec.ymm);
}
for (; progress != length; ++progress)
classes[progress] = (char_t)lookup.byte_to_class_group_vecs_[((u8_t)source[progress]) >> 4]
.u8s[((u8_t)source[progress]) & 0x0f];
}
};
template <typename score_type_, sz_similarity_objective_t objective_, sz_similarity_locality_t locality_>
struct diagonal_walker<char, score_type_, error_costs_32x32_t, affine_gap_costs_t, objective_, locality_,
sz_cap_haswell_k, void> {
using char_t = char;
using score_t = score_type_;
using substituter_t = error_costs_32x32_t;
using gap_costs_t = affine_gap_costs_t;
static constexpr sz_similarity_objective_t objective_k = objective_;
static constexpr sz_similarity_locality_t locality_k = locality_;
static constexpr sz_capability_t capability_k = sz_cap_haswell_k;
using tile_scorer_t = tile_scorer<char_t const *, char_t const *, score_t, substituter_t, gap_costs_t, objective_k,
locality_k, capability_k>;
substituter_t substituter_ {};
affine_gap_costs_t gap_costs_ {};
diagonal_walker() noexcept {}
diagonal_walker(substituter_t subs, affine_gap_costs_t gaps) noexcept : substituter_(subs), gap_costs_(gaps) {}
struct layout_t {
size_t previous_scores = 0; size_t current_scores = 0;
size_t next_scores = 0;
size_t current_inserts = 0; size_t next_inserts = 0;
size_t current_deletes = 0; size_t next_deletes = 0;
size_t shorter_reversed = 0; size_t shorter_reversed_classes = 0; size_t longer_classes = 0; size_t total = 0; constexpr operator size_t() const noexcept { return total; }
};
layout_t layout(span<char_t const> first, span<char_t const> second, cpu_specs_t const &specs) const noexcept {
size_t const shorter_length = sz_min_of_two(first.size(), second.size());
size_t const longer_length = sz_max_of_two(first.size(), second.size());
size_t const diagonal_bytes = sizeof(score_t) * (shorter_length + 1); size_t const shorter_stream_bytes = shorter_length + step_classes_k; size_t const longer_stream_bytes = longer_length + step_classes_k;
scratch_amount_t amount {specs.cache_line_width};
layout_t at;
at.previous_scores = amount, amount += diagonal_bytes;
at.current_scores = amount, amount += diagonal_bytes;
at.next_scores = amount, amount += diagonal_bytes;
at.current_inserts = amount, amount += diagonal_bytes;
at.next_inserts = amount, amount += diagonal_bytes;
at.current_deletes = amount, amount += diagonal_bytes;
at.next_deletes = amount, amount += diagonal_bytes;
at.shorter_reversed = amount, amount += shorter_stream_bytes;
at.shorter_reversed_classes = amount, amount += shorter_stream_bytes;
at.longer_classes = amount, amount += longer_stream_bytes;
at.total = amount;
return at;
}
template <typename executor_type_>
#if SZ_HAS_CONCEPTS_
requires executor_like<executor_type_>
#endif
status_t operator()(span<char_t const> const &first, span<char_t const> const &second, score_t &result_ref,
scratch_space_t scratch_space, executor_type_ &&executor,
cpu_specs_t const &specs) const noexcept {
if (first.empty() || second.empty()) {
result_ref = 0;
if constexpr (locality_k == sz_similarity_global_k) {
if (!first.empty() && second.empty()) {
result_ref = gap_costs_.open + gap_costs_.extend * (first.size() - 1);
}
else if (first.empty() && !second.empty()) {
result_ref = gap_costs_.open + gap_costs_.extend * (second.size() - 1);
}
}
return status_t::success_k;
}
char_t const *shorter = first.data(), *longer = second.data();
size_t shorter_length = first.size(), longer_length = second.size();
bool transpose = false;
if (shorter_length > longer_length) {
trivial_swap(shorter, longer);
trivial_swap(shorter_length, longer_length);
transpose = true;
}
size_t const shorter_dim = shorter_length + 1;
size_t const longer_dim = longer_length + 1;
size_t const diagonals_count = shorter_dim + longer_dim - 1;
size_t const max_diagonal_length = shorter_length + 1;
layout_t const at = layout(first, second, specs);
if (scratch_space.size() < at.total) return status_t::bad_alloc_k;
score_t *previous_scores = (score_t *)(scratch_space.data() + at.previous_scores);
score_t *current_scores = (score_t *)(scratch_space.data() + at.current_scores);
score_t *next_scores = (score_t *)(scratch_space.data() + at.next_scores);
score_t *current_inserts = (score_t *)(scratch_space.data() + at.current_inserts);
score_t *next_inserts = (score_t *)(scratch_space.data() + at.next_inserts);
score_t *current_deletes = (score_t *)(scratch_space.data() + at.current_deletes);
score_t *next_deletes = (score_t *)(scratch_space.data() + at.next_deletes);
char_t *const shorter_reversed = (char_t *)(scratch_space.data() + at.shorter_reversed);
char_t *const shorter_reversed_classes = (char_t *)(scratch_space.data() + at.shorter_reversed_classes);
char_t *const longer_classes = (char_t *)(scratch_space.data() + at.longer_classes);
for (size_t i = 0; i != shorter_length; ++i) shorter_reversed[i] = shorter[shorter_length - 1 - i];
tile_scorer_t scorer {substituter_, gap_costs_};
scorer.lookup_.reload_classes(substituter_.byte_to_class);
scorer.prepare(transpose);
classify_into_(scorer.lookup_, shorter_reversed, shorter_length, shorter_reversed_classes);
classify_into_(scorer.lookup_, longer, longer_length, longer_classes);
scorer.init_score(previous_scores[0], 0);
scorer.init_score(current_scores[0], 1);
scorer.init_score(current_scores[1], 1);
scorer.init_gap(current_inserts[0], 1);
scorer.init_gap(current_deletes[1], 1);
size_t next_diagonal_index = 2;
for (; next_diagonal_index < shorter_dim; ++next_diagonal_index) {
size_t const next_diagonal_length = next_diagonal_index + 1;
scorer( shorter_reversed_classes + shorter_length - next_diagonal_index + 1, longer_classes, next_diagonal_length - 2, previous_scores, current_scores, current_scores + 1, current_inserts, current_deletes + 1, next_scores + 1, next_inserts + 1, next_deletes + 1, executor);
scorer.init_score(next_scores[0], next_diagonal_index);
scorer.init_score(next_scores[next_diagonal_length - 1], next_diagonal_index);
scorer.init_gap(next_inserts[0], next_diagonal_index);
scorer.init_gap(next_deletes[next_diagonal_length - 1], next_diagonal_index);
rotate_three(previous_scores, current_scores, next_scores);
trivial_swap(current_inserts, next_inserts);
trivial_swap(current_deletes, next_deletes);
}
for (; next_diagonal_index < longer_dim; ++next_diagonal_index) {
size_t const next_diagonal_length = shorter_dim;
scorer( shorter_reversed_classes + shorter_length - shorter_dim + 1, longer_classes + next_diagonal_index - shorter_dim, next_diagonal_length - 1, previous_scores, current_scores, current_scores + 1, current_inserts, current_deletes + 1, next_scores, next_inserts, next_deletes, executor);
scorer.init_score(next_scores[next_diagonal_length - 1], next_diagonal_index);
scorer.init_gap(next_deletes[next_diagonal_length - 1], next_diagonal_index);
rotate_three(previous_scores, current_scores, next_scores);
trivial_swap(current_inserts, next_inserts);
trivial_swap(current_deletes, next_deletes);
sz_move_serial((ptr_t)(previous_scores), (ptr_t)(previous_scores + 1),
(max_diagonal_length - 1) * sizeof(score_t));
}
for (; next_diagonal_index < diagonals_count; ++next_diagonal_index) {
size_t const next_diagonal_length = diagonals_count - next_diagonal_index;
scorer( shorter_reversed_classes + shorter_length - shorter_dim + 1, longer_classes + next_diagonal_index - shorter_dim, next_diagonal_length, previous_scores, current_scores, current_scores + 1, current_inserts, current_deletes + 1, next_scores, next_inserts, next_deletes, executor);
rotate_three(previous_scores, current_scores, next_scores);
trivial_swap(current_inserts, next_inserts);
trivial_swap(current_deletes, next_deletes);
previous_scores++;
}
result_ref = scorer.score();
return status_t::success_k;
}
static constexpr size_t step_classes_k = 32;
private:
static void classify_into_(substitution_lookup_haswell_t const &lookup, char_t const *source, size_t length,
char_t *classes) noexcept {
u256_vec_t source_vec, classes_vec;
size_t progress = 0;
for (; progress + step_classes_k <= length; progress += step_classes_k) {
source_vec.ymm = _mm256_loadu_si256((__m256i const *)(source + progress));
classes_vec = lookup.classify32(source_vec);
_mm256_storeu_si256((__m256i *)(classes + progress), classes_vec.ymm);
}
for (; progress != length; ++progress)
classes[progress] = (char_t)lookup.byte_to_class_group_vecs_[((u8_t)source[progress]) >> 4]
.u8s[((u8_t)source[progress]) & 0x0f];
}
};
template <>
struct needleman_wunsch_score<char, error_costs_32x32_t, linear_gap_costs_t, sz_caps_sh_k> {
using char_t = char;
using substituter_t = error_costs_32x32_t;
using gap_costs_t = linear_gap_costs_t;
static constexpr size_t diagonal_buffers_count_k = 3;
using diagonal_i16_t = diagonal_walker<char_t, i16_t, substituter_t, gap_costs_t, sz_maximize_score_k,
sz_similarity_global_k, sz_cap_haswell_k>;
using diagonal_i32_t = diagonal_walker<char_t, i32_t, substituter_t, gap_costs_t, sz_maximize_score_k,
sz_similarity_global_k, sz_cap_haswell_k>;
using diagonal_i64_t = diagonal_walker<char_t, i64_t, substituter_t, gap_costs_t, sz_maximize_score_k,
sz_similarity_global_k, sz_cap_serial_k>;
substituter_t substituter_ {};
linear_gap_costs_t gap_costs_ {};
needleman_wunsch_score() noexcept {}
needleman_wunsch_score(substituter_t subs, linear_gap_costs_t gaps) noexcept
: substituter_(subs), gap_costs_(gaps) {}
size_t scratch_space_needed(span<char_t const> first, span<char_t const> second,
cpu_specs_t const &specs) const noexcept {
size_t const shorter_length = std::min(first.size(), second.size());
size_t const longer_length = std::max(first.size(), second.size());
size_t const max_diagonal_length = shorter_length + 1;
size_t const padded_diagonal_length =
round_up_to_multiple(sizeof(i64_t) * max_diagonal_length, specs.cache_line_width) / sizeof(i64_t);
size_t const padded_shorter_stream_length = round_up_to_multiple(
shorter_length + diagonal_i16_t::step_classes_k, specs.cache_line_width);
size_t const padded_longer_stream_length = round_up_to_multiple(longer_length + diagonal_i16_t::step_classes_k,
specs.cache_line_width);
return sizeof(i64_t) * padded_diagonal_length * diagonal_buffers_count_k + padded_shorter_stream_length * 2 +
padded_longer_stream_length;
}
template <typename executor_type_>
#if SZ_HAS_CONCEPTS_
requires executor_like<executor_type_>
#endif
status_t operator()(span<char_t const> const &first, span<char_t const> const &second, ssize_t &result_ref,
scratch_space_t scratch_space, executor_type_ &executor,
cpu_specs_t const &specs) const noexcept {
using diagonal_memory_requirements_t = diagonal_memory_requirements<ssize_t>;
diagonal_memory_requirements_t requirements( first.size(), second.size(), gap_type<gap_costs_t>(), substituter_.magnitude(), gap_costs_.magnitude(), sizeof(char_t), specs.cache_line_width);
if (requirements.bytes_per_cell <= 2) {
i16_t result_i16;
status_t status = diagonal_i16_t {substituter_, gap_costs_}(first, second, result_i16, scratch_space,
executor, specs);
if (status != status_t::success_k) return status;
result_ref = result_i16;
}
else if (requirements.bytes_per_cell == 4) {
i32_t result_i32;
status_t status = diagonal_i32_t {substituter_, gap_costs_}(first, second, result_i32, scratch_space,
executor, specs);
if (status != status_t::success_k) return status;
result_ref = result_i32;
}
else if (requirements.bytes_per_cell == 8) {
i64_t result_i64;
status_t status = diagonal_i64_t {substituter_, gap_costs_}(first, second, result_i64, scratch_space,
executor, specs);
if (status != status_t::success_k) return status;
result_ref = result_i64;
}
return status_t::success_k;
}
};
template <>
struct smith_waterman_score<char, error_costs_32x32_t, linear_gap_costs_t, sz_caps_sh_k> {
using char_t = char;
using substituter_t = error_costs_32x32_t;
using gap_costs_t = linear_gap_costs_t;
static constexpr size_t diagonal_buffers_count_k = 3;
using diagonal_i16_t = diagonal_walker<char_t, i16_t, substituter_t, linear_gap_costs_t, sz_maximize_score_k,
sz_similarity_local_k, sz_cap_haswell_k>;
using diagonal_i32_t = diagonal_walker<char_t, i32_t, substituter_t, linear_gap_costs_t, sz_maximize_score_k,
sz_similarity_local_k, sz_cap_haswell_k>;
using diagonal_i64_t = diagonal_walker<char_t, i64_t, substituter_t, linear_gap_costs_t, sz_maximize_score_k,
sz_similarity_local_k, sz_cap_serial_k>;
substituter_t substituter_ {};
linear_gap_costs_t gap_costs_ {};
smith_waterman_score() noexcept {}
smith_waterman_score(substituter_t subs, linear_gap_costs_t gaps) noexcept : substituter_(subs), gap_costs_(gaps) {}
size_t scratch_space_needed(span<char_t const> first, span<char_t const> second,
cpu_specs_t const &specs) const noexcept {
size_t const shorter_length = std::min(first.size(), second.size());
size_t const longer_length = std::max(first.size(), second.size());
size_t const max_diagonal_length = shorter_length + 1;
size_t const padded_diagonal_length =
round_up_to_multiple(sizeof(i64_t) * max_diagonal_length, specs.cache_line_width) / sizeof(i64_t);
size_t const padded_shorter_stream_length = round_up_to_multiple(
shorter_length + diagonal_i16_t::step_classes_k, specs.cache_line_width);
size_t const padded_longer_stream_length = round_up_to_multiple(longer_length + diagonal_i16_t::step_classes_k,
specs.cache_line_width);
return sizeof(i64_t) * padded_diagonal_length * diagonal_buffers_count_k + padded_shorter_stream_length * 2 +
padded_longer_stream_length;
}
template <typename executor_type_>
#if SZ_HAS_CONCEPTS_
requires executor_like<executor_type_>
#endif
status_t operator()(span<char_t const> const &first, span<char_t const> const &second, ssize_t &result_ref,
scratch_space_t scratch_space, executor_type_ &executor,
cpu_specs_t const &specs) const noexcept {
using diagonal_memory_requirements_t = diagonal_memory_requirements<ssize_t>;
diagonal_memory_requirements_t requirements( first.size(), second.size(), gap_type<gap_costs_t>(), substituter_.magnitude(), gap_costs_.magnitude(), sizeof(char_t), specs.cache_line_width);
if (requirements.bytes_per_cell <= 2) {
i16_t result_i16;
status_t status = diagonal_i16_t {substituter_, gap_costs_}(first, second, result_i16, scratch_space,
executor, specs);
if (status != status_t::success_k) return status;
result_ref = result_i16;
}
else if (requirements.bytes_per_cell == 4) {
i32_t result_i32;
status_t status = diagonal_i32_t {substituter_, gap_costs_}(first, second, result_i32, scratch_space,
executor, specs);
if (status != status_t::success_k) return status;
result_ref = result_i32;
}
else if (requirements.bytes_per_cell == 8) {
i64_t result_i64;
status_t status = diagonal_i64_t {substituter_, gap_costs_}(first, second, result_i64, scratch_space,
executor, specs);
if (status != status_t::success_k) return status;
result_ref = result_i64;
}
return status_t::success_k;
}
};
template <>
struct needleman_wunsch_score<char, error_costs_32x32_t, affine_gap_costs_t, sz_caps_sh_k> {
using char_t = char;
using substituter_t = error_costs_32x32_t;
using gap_costs_t = affine_gap_costs_t;
static constexpr size_t diagonal_buffers_count_k = 7;
using diagonal_i16_t = diagonal_walker<char_t, i16_t, substituter_t, gap_costs_t, sz_maximize_score_k,
sz_similarity_global_k, sz_cap_haswell_k>;
using diagonal_i32_t = diagonal_walker<char_t, i32_t, substituter_t, gap_costs_t, sz_maximize_score_k,
sz_similarity_global_k, sz_cap_haswell_k>;
using diagonal_i64_t = diagonal_walker<char_t, i64_t, substituter_t, gap_costs_t, sz_maximize_score_k,
sz_similarity_global_k, sz_cap_serial_k>;
substituter_t substituter_ {};
affine_gap_costs_t gap_costs_ {};
needleman_wunsch_score() noexcept {}
needleman_wunsch_score(substituter_t subs, affine_gap_costs_t gaps) noexcept
: substituter_(subs), gap_costs_(gaps) {}
size_t scratch_space_needed(span<char_t const> first, span<char_t const> second,
cpu_specs_t const &specs) const noexcept {
size_t const shorter_length = std::min(first.size(), second.size());
size_t const longer_length = std::max(first.size(), second.size());
size_t const max_diagonal_length = shorter_length + 1;
size_t const padded_diagonal_length =
round_up_to_multiple(sizeof(i64_t) * max_diagonal_length, specs.cache_line_width) / sizeof(i64_t);
size_t const padded_shorter_stream_length = round_up_to_multiple(
shorter_length + diagonal_i16_t::step_classes_k, specs.cache_line_width);
size_t const padded_longer_stream_length = round_up_to_multiple(longer_length + diagonal_i16_t::step_classes_k,
specs.cache_line_width);
return sizeof(i64_t) * padded_diagonal_length * diagonal_buffers_count_k + padded_shorter_stream_length * 2 +
padded_longer_stream_length;
}
template <typename executor_type_>
#if SZ_HAS_CONCEPTS_
requires executor_like<executor_type_>
#endif
status_t operator()(span<char_t const> const &first, span<char_t const> const &second, ssize_t &result_ref,
scratch_space_t scratch_space, executor_type_ &executor,
cpu_specs_t const &specs) const noexcept {
using diagonal_memory_requirements_t = diagonal_memory_requirements<ssize_t>;
diagonal_memory_requirements_t requirements( first.size(), second.size(), gap_type<gap_costs_t>(), substituter_.magnitude(), gap_costs_.magnitude(), sizeof(char_t), specs.cache_line_width);
if (requirements.bytes_per_cell <= 2) {
i16_t result_i16;
status_t status = diagonal_i16_t {substituter_, gap_costs_}(first, second, result_i16, scratch_space,
executor, specs);
if (status != status_t::success_k) return status;
result_ref = result_i16;
}
else if (requirements.bytes_per_cell == 4) {
i32_t result_i32;
status_t status = diagonal_i32_t {substituter_, gap_costs_}(first, second, result_i32, scratch_space,
executor, specs);
if (status != status_t::success_k) return status;
result_ref = result_i32;
}
else if (requirements.bytes_per_cell == 8) {
i64_t result_i64;
status_t status = diagonal_i64_t {substituter_, gap_costs_}(first, second, result_i64, scratch_space,
executor, specs);
if (status != status_t::success_k) return status;
result_ref = result_i64;
}
return status_t::success_k;
}
};
template <>
struct smith_waterman_score<char, error_costs_32x32_t, affine_gap_costs_t, sz_caps_sh_k> {
using char_t = char;
using substituter_t = error_costs_32x32_t;
using gap_costs_t = affine_gap_costs_t;
static constexpr size_t diagonal_buffers_count_k = 7;
using diagonal_i16_t = diagonal_walker<char_t, i16_t, substituter_t, affine_gap_costs_t, sz_maximize_score_k,
sz_similarity_local_k, sz_cap_haswell_k>;
using diagonal_i32_t = diagonal_walker<char_t, i32_t, substituter_t, affine_gap_costs_t, sz_maximize_score_k,
sz_similarity_local_k, sz_cap_haswell_k>;
using diagonal_i64_t = diagonal_walker<char_t, i64_t, substituter_t, affine_gap_costs_t, sz_maximize_score_k,
sz_similarity_local_k, sz_cap_serial_k>;
substituter_t substituter_ {};
affine_gap_costs_t gap_costs_ {};
smith_waterman_score() noexcept {}
smith_waterman_score(substituter_t subs, affine_gap_costs_t gaps) noexcept : substituter_(subs), gap_costs_(gaps) {}
size_t scratch_space_needed(span<char_t const> first, span<char_t const> second,
cpu_specs_t const &specs) const noexcept {
size_t const shorter_length = std::min(first.size(), second.size());
size_t const longer_length = std::max(first.size(), second.size());
size_t const max_diagonal_length = shorter_length + 1;
size_t const padded_diagonal_length =
round_up_to_multiple(sizeof(i64_t) * max_diagonal_length, specs.cache_line_width) / sizeof(i64_t);
size_t const padded_shorter_stream_length = round_up_to_multiple(
shorter_length + diagonal_i16_t::step_classes_k, specs.cache_line_width);
size_t const padded_longer_stream_length = round_up_to_multiple(longer_length + diagonal_i16_t::step_classes_k,
specs.cache_line_width);
return sizeof(i64_t) * padded_diagonal_length * diagonal_buffers_count_k + padded_shorter_stream_length * 2 +
padded_longer_stream_length;
}
template <typename executor_type_>
#if SZ_HAS_CONCEPTS_
requires executor_like<executor_type_>
#endif
status_t operator()(span<char_t const> const &first, span<char_t const> const &second, ssize_t &result_ref,
scratch_space_t scratch_space, executor_type_ &executor,
cpu_specs_t const &specs) const noexcept {
using diagonal_memory_requirements_t = diagonal_memory_requirements<ssize_t>;
diagonal_memory_requirements_t requirements( first.size(), second.size(), gap_type<gap_costs_t>(), substituter_.magnitude(), gap_costs_.magnitude(), sizeof(char_t), specs.cache_line_width);
if (requirements.bytes_per_cell <= 2) {
i16_t result_i16;
status_t status = diagonal_i16_t {substituter_, gap_costs_}(first, second, result_i16, scratch_space,
executor, specs);
if (status != status_t::success_k) return status;
result_ref = result_i16;
}
else if (requirements.bytes_per_cell == 4) {
i32_t result_i32;
status_t status = diagonal_i32_t {substituter_, gap_costs_}(first, second, result_i32, scratch_space,
executor, specs);
if (status != status_t::success_k) return status;
result_ref = result_i32;
}
else if (requirements.bytes_per_cell == 8) {
i64_t result_i64;
status_t status = diagonal_i64_t {substituter_, gap_costs_}(first, second, result_i64, scratch_space,
executor, specs);
if (status != status_t::success_k) return status;
result_ref = result_i64;
}
return status_t::success_k;
}
};
#pragma region Inter Sequence Candidate Lanes
template <sz_similarity_objective_t objective_>
struct candidate_lane_walker<char, u16_t, uniform_substitution_costs_t, linear_gap_costs_t, objective_,
sz_similarity_global_k, sz_cap_haswell_k, 16, void> {
using char_t = char;
using score_t = u16_t;
using substituter_t = uniform_substitution_costs_t;
using gap_costs_t = linear_gap_costs_t;
static constexpr sz_similarity_objective_t objective_k = objective_;
static constexpr sz_similarity_locality_t locality_k = sz_similarity_global_k;
static constexpr sz_capability_t capability_k = sz_cap_haswell_k;
static constexpr size_t candidate_lanes_k = 16;
static constexpr size_t capacity_k = (size_t)std::numeric_limits<score_t>::max();
static_assert(objective_ == sz_minimize_distance_k,
"The 16-bit candidate-lane kernel only implements distance minimization (Levenshtein).");
substituter_t substituter_ {};
linear_gap_costs_t gap_costs_ {};
candidate_lane_walker() noexcept {}
candidate_lane_walker(substituter_t subs, linear_gap_costs_t gaps) noexcept
: substituter_(subs), gap_costs_(gaps) {}
size_t scratch_space_needed(size_t longest_candidate, cpu_specs_t const &specs) const noexcept {
size_t const row_bytes = candidate_lanes_k * (longest_candidate + 1) * sizeof(score_t);
scratch_amount_t amount {specs.cache_line_width};
amount += row_bytes; amount += row_bytes; return amount;
}
status_t operator()(span<char_t const> query, candidate_lanes_block<char_t> candidates, score_t *result_lanes,
scratch_space_t scratch_space, cpu_specs_t const &specs) const noexcept {
sz_unused_(specs);
size_t const query_length = query.size();
size_t const longest_candidate = candidates.longest_candidate;
size_t const row_stride = candidate_lanes_k * (longest_candidate + 1);
score_t *previous_row = reinterpret_cast<score_t *>(scratch_space.data());
score_t *current_row = previous_row + row_stride;
score_t const match_cost = static_cast<score_t>(substituter_.match);
score_t const mismatch_cost = static_cast<score_t>(substituter_.mismatch);
score_t const gap_cost = static_cast<score_t>(gap_costs_.open_or_extend);
__m256i const match_vec = _mm256_set1_epi16(static_cast<short>(match_cost));
__m256i const mismatch_vec = _mm256_set1_epi16(static_cast<short>(mismatch_cost));
__m256i const gap_vec = _mm256_set1_epi16(static_cast<short>(gap_cost));
for (size_t column = 0; column <= longest_candidate; ++column)
_mm256_storeu_si256(reinterpret_cast<__m256i *>(previous_row + column * candidate_lanes_k),
_mm256_set1_epi16(static_cast<short>(static_cast<u16_t>(column * gap_cost))));
for (size_t query_position = 1; query_position <= query_length; ++query_position) {
__m128i const query_char_vec = _mm_set1_epi8(query[query_position - 1]);
_mm256_storeu_si256(reinterpret_cast<__m256i *>(current_row),
_mm256_set1_epi16(static_cast<short>(static_cast<u16_t>(query_position * gap_cost))));
for (size_t column = 1; column <= longest_candidate; ++column) {
__m128i const candidate_chars_vec = _mm_loadu_si128(
reinterpret_cast<__m128i const *>(candidates.position(column - 1)));
__m256i const diagonal_vec = _mm256_loadu_si256(
reinterpret_cast<__m256i const *>(previous_row + (column - 1) * candidate_lanes_k));
__m256i const deletion_source_vec = _mm256_loadu_si256(
reinterpret_cast<__m256i const *>(previous_row + column * candidate_lanes_k));
__m256i const insertion_source_vec = _mm256_loadu_si256(
reinterpret_cast<__m256i const *>(current_row + (column - 1) * candidate_lanes_k));
__m128i const equal_i8_vec = _mm_cmpeq_epi8(query_char_vec, candidate_chars_vec);
__m256i const equal_i16_vec = _mm256_cvtepi8_epi16(equal_i8_vec);
__m256i const mismatch_addend_vec = _mm256_blendv_epi8(mismatch_vec, match_vec, equal_i16_vec);
__m256i const cost_if_substitution_vec = _mm256_add_epi16(diagonal_vec, mismatch_addend_vec);
__m256i const cost_if_deletion_vec = _mm256_add_epi16(deletion_source_vec, gap_vec);
__m256i const cost_if_insertion_vec = _mm256_add_epi16(insertion_source_vec, gap_vec);
__m256i const cell_score_vec = _mm256_min_epu16(
cost_if_substitution_vec, _mm256_min_epu16(cost_if_deletion_vec, cost_if_insertion_vec));
_mm256_storeu_si256(reinterpret_cast<__m256i *>(current_row + column * candidate_lanes_k),
cell_score_vec);
}
trivial_swap(previous_row, current_row);
}
for (size_t lane_index = 0; lane_index < candidates.lanes_count; ++lane_index) {
size_t const candidate_length = candidates.lengths[lane_index];
result_lanes[lane_index] = previous_row[candidate_length * candidate_lanes_k + lane_index];
}
return status_t::success_k;
}
};
template <sz_similarity_objective_t objective_>
struct candidate_lane_walker<char, u32_t, uniform_substitution_costs_t, linear_gap_costs_t, objective_,
sz_similarity_global_k, sz_cap_haswell_k, 8, void> {
using char_t = char;
using score_t = u32_t;
using substituter_t = uniform_substitution_costs_t;
using gap_costs_t = linear_gap_costs_t;
static constexpr sz_similarity_objective_t objective_k = objective_;
static constexpr sz_similarity_locality_t locality_k = sz_similarity_global_k;
static constexpr sz_capability_t capability_k = sz_cap_haswell_k;
static constexpr size_t candidate_lanes_k = 8;
static constexpr size_t capacity_k = (size_t)std::numeric_limits<score_t>::max();
static_assert(objective_ == sz_minimize_distance_k,
"The 32-bit candidate-lane kernel only implements distance minimization (Levenshtein).");
substituter_t substituter_ {};
linear_gap_costs_t gap_costs_ {};
candidate_lane_walker() noexcept {}
candidate_lane_walker(substituter_t subs, linear_gap_costs_t gaps) noexcept
: substituter_(subs), gap_costs_(gaps) {}
size_t scratch_space_needed(size_t longest_candidate, cpu_specs_t const &specs) const noexcept {
size_t const row_bytes = candidate_lanes_k * (longest_candidate + 1) * sizeof(score_t);
scratch_amount_t amount {specs.cache_line_width};
amount += row_bytes; amount += row_bytes; return amount;
}
status_t operator()(span<char_t const> query, candidate_lanes_block<char_t> candidates, score_t *result_lanes,
scratch_space_t scratch_space, cpu_specs_t const &specs) const noexcept {
sz_unused_(specs);
size_t const query_length = query.size();
size_t const longest_candidate = candidates.longest_candidate;
size_t const row_stride = candidate_lanes_k * (longest_candidate + 1);
score_t *previous_row = reinterpret_cast<score_t *>(scratch_space.data());
score_t *current_row = previous_row + row_stride;
score_t const match_cost = static_cast<score_t>(substituter_.match);
score_t const mismatch_cost = static_cast<score_t>(substituter_.mismatch);
score_t const gap_cost = static_cast<score_t>(gap_costs_.open_or_extend);
__m256i const match_vec = _mm256_set1_epi32(static_cast<int>(match_cost));
__m256i const mismatch_vec = _mm256_set1_epi32(static_cast<int>(mismatch_cost));
__m256i const gap_vec = _mm256_set1_epi32(static_cast<int>(gap_cost));
for (size_t column = 0; column <= longest_candidate; ++column)
_mm256_storeu_si256(reinterpret_cast<__m256i *>(previous_row + column * candidate_lanes_k),
_mm256_set1_epi32(static_cast<int>(static_cast<u32_t>(column * gap_cost))));
for (size_t query_position = 1; query_position <= query_length; ++query_position) {
__m128i const query_char_vec = _mm_set1_epi8(query[query_position - 1]);
_mm256_storeu_si256(reinterpret_cast<__m256i *>(current_row),
_mm256_set1_epi32(static_cast<int>(static_cast<u32_t>(query_position * gap_cost))));
for (size_t column = 1; column <= longest_candidate; ++column) {
__m128i const candidate_chars_vec = _mm_loadl_epi64(
reinterpret_cast<__m128i const *>(candidates.position(column - 1)));
__m256i const diagonal_vec = _mm256_loadu_si256(
reinterpret_cast<__m256i const *>(previous_row + (column - 1) * candidate_lanes_k));
__m256i const deletion_source_vec = _mm256_loadu_si256(
reinterpret_cast<__m256i const *>(previous_row + column * candidate_lanes_k));
__m256i const insertion_source_vec = _mm256_loadu_si256(
reinterpret_cast<__m256i const *>(current_row + (column - 1) * candidate_lanes_k));
__m128i const equal_i8_vec = _mm_cmpeq_epi8(query_char_vec, candidate_chars_vec);
__m256i const equal_i32_vec = _mm256_cvtepi8_epi32(equal_i8_vec);
__m256i const mismatch_addend_vec = _mm256_blendv_epi8(mismatch_vec, match_vec, equal_i32_vec);
__m256i const cost_if_substitution_vec = _mm256_add_epi32(diagonal_vec, mismatch_addend_vec);
__m256i const cost_if_deletion_vec = _mm256_add_epi32(deletion_source_vec, gap_vec);
__m256i const cost_if_insertion_vec = _mm256_add_epi32(insertion_source_vec, gap_vec);
__m256i const cell_score_vec = _mm256_min_epu32(
cost_if_substitution_vec, _mm256_min_epu32(cost_if_deletion_vec, cost_if_insertion_vec));
_mm256_storeu_si256(reinterpret_cast<__m256i *>(current_row + column * candidate_lanes_k),
cell_score_vec);
}
trivial_swap(previous_row, current_row);
}
for (size_t lane_index = 0; lane_index < candidates.lanes_count; ++lane_index) {
size_t const candidate_length = candidates.lengths[lane_index];
result_lanes[lane_index] = previous_row[candidate_length * candidate_lanes_k + lane_index];
}
return status_t::success_k;
}
};
template <sz_similarity_objective_t objective_>
struct candidate_lane_walker<char, u16_t, uniform_substitution_costs_t, affine_gap_costs_t, objective_,
sz_similarity_global_k, sz_cap_haswell_k, 16, void> {
using char_t = char;
using score_t = u16_t;
using substituter_t = uniform_substitution_costs_t;
using gap_costs_t = affine_gap_costs_t;
static constexpr sz_similarity_objective_t objective_k = objective_;
static constexpr sz_similarity_locality_t locality_k = sz_similarity_global_k;
static constexpr sz_capability_t capability_k = sz_cap_haswell_k;
static constexpr size_t candidate_lanes_k = 16;
static constexpr size_t capacity_k = (size_t)std::numeric_limits<score_t>::max();
static_assert(objective_ == sz_minimize_distance_k,
"The 16-bit affine candidate-lane kernel only implements distance minimization (Levenshtein).");
substituter_t substituter_ {};
affine_gap_costs_t gap_costs_ {};
candidate_lane_walker() noexcept {}
candidate_lane_walker(substituter_t subs, affine_gap_costs_t gaps) noexcept
: substituter_(subs), gap_costs_(gaps) {}
size_t scratch_space_needed(size_t longest_candidate, cpu_specs_t const &specs) const noexcept {
size_t const score_row_bytes = candidate_lanes_k * (longest_candidate + 1) * sizeof(score_t);
scratch_amount_t amount {specs.cache_line_width};
amount += score_row_bytes; amount += score_row_bytes; amount += score_row_bytes; return amount;
}
status_t operator()(span<char_t const> query, candidate_lanes_block<char_t> candidates, score_t *result_lanes,
scratch_space_t scratch_space, cpu_specs_t const &specs) const noexcept {
sz_unused_(specs);
size_t const query_length = query.size();
size_t const longest_candidate = candidates.longest_candidate;
size_t const row_stride = candidate_lanes_k * (longest_candidate + 1);
score_t *previous_row = reinterpret_cast<score_t *>(scratch_space.data());
score_t *current_row = previous_row + row_stride;
score_t *vertical_row = current_row + row_stride;
score_t const match_cost = static_cast<score_t>(substituter_.match);
score_t const mismatch_cost = static_cast<score_t>(substituter_.mismatch);
score_t const open = static_cast<score_t>(gap_costs_.open);
score_t const extend = static_cast<score_t>(gap_costs_.extend);
__m256i const match_vec = _mm256_set1_epi16(static_cast<short>(match_cost));
__m256i const mismatch_vec = _mm256_set1_epi16(static_cast<short>(mismatch_cost));
__m256i const open_vec = _mm256_set1_epi16(static_cast<short>(open));
__m256i const extend_vec = _mm256_set1_epi16(static_cast<short>(extend));
__m256i const discard_bias_vec = _mm256_set1_epi16(static_cast<short>(static_cast<u16_t>(open + extend)));
_mm256_storeu_si256(reinterpret_cast<__m256i *>(previous_row), _mm256_setzero_si256());
_mm256_storeu_si256(reinterpret_cast<__m256i *>(vertical_row), discard_bias_vec);
for (size_t column = 1; column <= longest_candidate; ++column) {
__m256i const boundary_vec = _mm256_set1_epi16(
static_cast<short>(static_cast<u16_t>(open + extend * (u16_t)(column - 1))));
_mm256_storeu_si256(reinterpret_cast<__m256i *>(previous_row + column * candidate_lanes_k), boundary_vec);
_mm256_storeu_si256(reinterpret_cast<__m256i *>(vertical_row + column * candidate_lanes_k),
_mm256_add_epi16(discard_bias_vec, boundary_vec));
}
for (size_t query_position = 1; query_position <= query_length; ++query_position) {
__m128i const query_char_vec = _mm_set1_epi8(query[query_position - 1]);
__m256i const left_boundary_vec = _mm256_set1_epi16(
static_cast<short>(static_cast<u16_t>(open + extend * (u16_t)(query_position - 1))));
_mm256_storeu_si256(reinterpret_cast<__m256i *>(current_row), left_boundary_vec);
__m256i horizontal_vec = _mm256_add_epi16(discard_bias_vec, left_boundary_vec);
for (size_t column = 1; column <= longest_candidate; ++column) {
__m128i const candidate_chars_vec = _mm_loadu_si128(
reinterpret_cast<__m128i const *>(candidates.position(column - 1)));
__m256i const diagonal_vec = _mm256_loadu_si256(
reinterpret_cast<__m256i const *>(previous_row + (column - 1) * candidate_lanes_k));
__m256i const up_vec = _mm256_loadu_si256(
reinterpret_cast<__m256i const *>(previous_row + column * candidate_lanes_k));
__m256i const left_vec = _mm256_loadu_si256(
reinterpret_cast<__m256i const *>(current_row + (column - 1) * candidate_lanes_k));
__m256i const up_vertical_vec = _mm256_loadu_si256(
reinterpret_cast<__m256i const *>(vertical_row + column * candidate_lanes_k));
__m128i const equal_i8_vec = _mm_cmpeq_epi8(query_char_vec, candidate_chars_vec);
__m256i const equal_i16_vec = _mm256_cvtepi8_epi16(equal_i8_vec);
__m256i const substitution_addend_vec = _mm256_blendv_epi8(mismatch_vec, match_vec, equal_i16_vec);
__m256i const cost_if_substitution_vec = _mm256_add_epi16(diagonal_vec, substitution_addend_vec);
__m256i const vertical_vec = _mm256_min_epu16(_mm256_add_epi16(up_vec, open_vec),
_mm256_add_epi16(up_vertical_vec, extend_vec));
horizontal_vec = _mm256_min_epu16(_mm256_add_epi16(left_vec, open_vec),
_mm256_add_epi16(horizontal_vec, extend_vec));
__m256i const cost_if_gap_vec = _mm256_min_epu16(vertical_vec, horizontal_vec);
__m256i const cell_score_vec = _mm256_min_epu16(cost_if_substitution_vec, cost_if_gap_vec);
_mm256_storeu_si256(reinterpret_cast<__m256i *>(vertical_row + column * candidate_lanes_k),
vertical_vec);
_mm256_storeu_si256(reinterpret_cast<__m256i *>(current_row + column * candidate_lanes_k),
cell_score_vec);
}
trivial_swap(previous_row, current_row);
}
for (size_t lane_index = 0; lane_index < candidates.lanes_count; ++lane_index) {
size_t const candidate_length = candidates.lengths[lane_index];
result_lanes[lane_index] = previous_row[candidate_length * candidate_lanes_k + lane_index];
}
return status_t::success_k;
}
};
template <sz_similarity_objective_t objective_>
struct candidate_lane_walker<char, u32_t, uniform_substitution_costs_t, affine_gap_costs_t, objective_,
sz_similarity_global_k, sz_cap_haswell_k, 8, void> {
using char_t = char;
using score_t = u32_t;
using substituter_t = uniform_substitution_costs_t;
using gap_costs_t = affine_gap_costs_t;
static constexpr sz_similarity_objective_t objective_k = objective_;
static constexpr sz_similarity_locality_t locality_k = sz_similarity_global_k;
static constexpr sz_capability_t capability_k = sz_cap_haswell_k;
static constexpr size_t candidate_lanes_k = 8;
static constexpr size_t capacity_k = (size_t)std::numeric_limits<score_t>::max();
static_assert(objective_ == sz_minimize_distance_k,
"The 32-bit affine candidate-lane kernel only implements distance minimization (Levenshtein).");
substituter_t substituter_ {};
affine_gap_costs_t gap_costs_ {};
candidate_lane_walker() noexcept {}
candidate_lane_walker(substituter_t subs, affine_gap_costs_t gaps) noexcept
: substituter_(subs), gap_costs_(gaps) {}
size_t scratch_space_needed(size_t longest_candidate, cpu_specs_t const &specs) const noexcept {
size_t const score_row_bytes = candidate_lanes_k * (longest_candidate + 1) * sizeof(score_t);
scratch_amount_t amount {specs.cache_line_width};
amount += score_row_bytes; amount += score_row_bytes; amount += score_row_bytes; return amount;
}
status_t operator()(span<char_t const> query, candidate_lanes_block<char_t> candidates, score_t *result_lanes,
scratch_space_t scratch_space, cpu_specs_t const &specs) const noexcept {
sz_unused_(specs);
size_t const query_length = query.size();
size_t const longest_candidate = candidates.longest_candidate;
size_t const row_stride = candidate_lanes_k * (longest_candidate + 1);
score_t *previous_row = reinterpret_cast<score_t *>(scratch_space.data());
score_t *current_row = previous_row + row_stride;
score_t *vertical_row = current_row + row_stride;
score_t const match_cost = static_cast<score_t>(substituter_.match);
score_t const mismatch_cost = static_cast<score_t>(substituter_.mismatch);
score_t const open = static_cast<score_t>(gap_costs_.open);
score_t const extend = static_cast<score_t>(gap_costs_.extend);
__m256i const match_vec = _mm256_set1_epi32(static_cast<int>(match_cost));
__m256i const mismatch_vec = _mm256_set1_epi32(static_cast<int>(mismatch_cost));
__m256i const open_vec = _mm256_set1_epi32(static_cast<int>(open));
__m256i const extend_vec = _mm256_set1_epi32(static_cast<int>(extend));
__m256i const discard_bias_vec = _mm256_set1_epi32(static_cast<int>(static_cast<u32_t>(open + extend)));
_mm256_storeu_si256(reinterpret_cast<__m256i *>(previous_row), _mm256_setzero_si256());
_mm256_storeu_si256(reinterpret_cast<__m256i *>(vertical_row), discard_bias_vec);
for (size_t column = 1; column <= longest_candidate; ++column) {
__m256i const boundary_vec = _mm256_set1_epi32(
static_cast<int>(static_cast<u32_t>(open + extend * (u32_t)(column - 1))));
_mm256_storeu_si256(reinterpret_cast<__m256i *>(previous_row + column * candidate_lanes_k), boundary_vec);
_mm256_storeu_si256(reinterpret_cast<__m256i *>(vertical_row + column * candidate_lanes_k),
_mm256_add_epi32(discard_bias_vec, boundary_vec));
}
for (size_t query_position = 1; query_position <= query_length; ++query_position) {
__m128i const query_char_vec = _mm_set1_epi8(query[query_position - 1]);
__m256i const left_boundary_vec = _mm256_set1_epi32(
static_cast<int>(static_cast<u32_t>(open + extend * (u32_t)(query_position - 1))));
_mm256_storeu_si256(reinterpret_cast<__m256i *>(current_row), left_boundary_vec);
__m256i horizontal_vec = _mm256_add_epi32(discard_bias_vec, left_boundary_vec);
for (size_t column = 1; column <= longest_candidate; ++column) {
__m128i const candidate_chars_vec = _mm_loadl_epi64(
reinterpret_cast<__m128i const *>(candidates.position(column - 1)));
__m256i const diagonal_vec = _mm256_loadu_si256(
reinterpret_cast<__m256i const *>(previous_row + (column - 1) * candidate_lanes_k));
__m256i const up_vec = _mm256_loadu_si256(
reinterpret_cast<__m256i const *>(previous_row + column * candidate_lanes_k));
__m256i const left_vec = _mm256_loadu_si256(
reinterpret_cast<__m256i const *>(current_row + (column - 1) * candidate_lanes_k));
__m256i const up_vertical_vec = _mm256_loadu_si256(
reinterpret_cast<__m256i const *>(vertical_row + column * candidate_lanes_k));
__m128i const equal_i8_vec = _mm_cmpeq_epi8(query_char_vec, candidate_chars_vec);
__m256i const equal_i32_vec = _mm256_cvtepi8_epi32(equal_i8_vec);
__m256i const substitution_addend_vec = _mm256_blendv_epi8(mismatch_vec, match_vec, equal_i32_vec);
__m256i const cost_if_substitution_vec = _mm256_add_epi32(diagonal_vec, substitution_addend_vec);
__m256i const vertical_vec = _mm256_min_epu32(_mm256_add_epi32(up_vec, open_vec),
_mm256_add_epi32(up_vertical_vec, extend_vec));
horizontal_vec = _mm256_min_epu32(_mm256_add_epi32(left_vec, open_vec),
_mm256_add_epi32(horizontal_vec, extend_vec));
__m256i const cost_if_gap_vec = _mm256_min_epu32(vertical_vec, horizontal_vec);
__m256i const cell_score_vec = _mm256_min_epu32(cost_if_substitution_vec, cost_if_gap_vec);
_mm256_storeu_si256(reinterpret_cast<__m256i *>(vertical_row + column * candidate_lanes_k),
vertical_vec);
_mm256_storeu_si256(reinterpret_cast<__m256i *>(current_row + column * candidate_lanes_k),
cell_score_vec);
}
trivial_swap(previous_row, current_row);
}
for (size_t lane_index = 0; lane_index < candidates.lanes_count; ++lane_index) {
size_t const candidate_length = candidates.lengths[lane_index];
result_lanes[lane_index] = previous_row[candidate_length * candidate_lanes_k + lane_index];
}
return status_t::success_k;
}
};
template <sz_similarity_objective_t objective_>
struct candidate_lane_walker<rune_t, u16_t, uniform_substitution_costs_t, linear_gap_costs_t, objective_,
sz_similarity_global_k, sz_cap_haswell_k, 16, void> {
using char_t = rune_t;
using score_t = u16_t;
using substituter_t = uniform_substitution_costs_t;
using gap_costs_t = linear_gap_costs_t;
static constexpr sz_similarity_objective_t objective_k = objective_;
static constexpr sz_similarity_locality_t locality_k = sz_similarity_global_k;
static constexpr sz_capability_t capability_k = sz_cap_haswell_k;
static constexpr size_t candidate_lanes_k = 16;
static constexpr size_t capacity_k = (size_t)std::numeric_limits<score_t>::max();
static constexpr size_t runes_per_vec_k = 8;
static_assert(objective_ == sz_minimize_distance_k,
"The 16-bit rune candidate-lane kernel only implements distance minimization (Levenshtein).");
substituter_t substituter_ {};
linear_gap_costs_t gap_costs_ {};
candidate_lane_walker() noexcept {}
candidate_lane_walker(substituter_t subs, linear_gap_costs_t gaps) noexcept
: substituter_(subs), gap_costs_(gaps) {}
size_t scratch_space_needed(size_t longest_candidate, cpu_specs_t const &specs) const noexcept {
size_t const row_bytes = candidate_lanes_k * (longest_candidate + 1) * sizeof(score_t);
scratch_amount_t amount {specs.cache_line_width};
amount += row_bytes; amount += row_bytes; return amount;
}
status_t operator()(span<char_t const> query, candidate_lanes_block<char_t> candidates, score_t *result_lanes,
scratch_space_t scratch_space, cpu_specs_t const &specs) const noexcept {
sz_unused_(specs);
size_t const query_length = query.size();
size_t const longest_candidate = candidates.longest_candidate;
size_t const row_stride = candidate_lanes_k * (longest_candidate + 1);
score_t *previous_row = reinterpret_cast<score_t *>(scratch_space.data());
score_t *current_row = previous_row + row_stride;
score_t const match_cost = static_cast<score_t>(substituter_.match);
score_t const mismatch_cost = static_cast<score_t>(substituter_.mismatch);
score_t const gap_cost = static_cast<score_t>(gap_costs_.open_or_extend);
__m256i const match_vec = _mm256_set1_epi16(static_cast<short>(match_cost));
__m256i const mismatch_vec = _mm256_set1_epi16(static_cast<short>(mismatch_cost));
__m256i const gap_vec = _mm256_set1_epi16(static_cast<short>(gap_cost));
for (size_t column = 0; column <= longest_candidate; ++column)
_mm256_storeu_si256(reinterpret_cast<__m256i *>(previous_row + column * candidate_lanes_k),
_mm256_set1_epi16(static_cast<short>(static_cast<u16_t>(column * gap_cost))));
for (size_t query_position = 1; query_position <= query_length; ++query_position) {
__m256i const query_rune_vec = _mm256_set1_epi32(
static_cast<int>(static_cast<u32_t>(query[query_position - 1])));
_mm256_storeu_si256(reinterpret_cast<__m256i *>(current_row),
_mm256_set1_epi16(static_cast<short>(static_cast<u16_t>(query_position * gap_cost))));
for (size_t column = 1; column <= longest_candidate; ++column) {
__m256i const candidate_runes_low_vec = _mm256_loadu_si256(
reinterpret_cast<__m256i const *>(candidates.position(column - 1)));
__m256i const candidate_runes_high_vec = _mm256_loadu_si256(
reinterpret_cast<__m256i const *>(candidates.position(column - 1) + runes_per_vec_k));
__m256i const diagonal_vec = _mm256_loadu_si256(
reinterpret_cast<__m256i const *>(previous_row + (column - 1) * candidate_lanes_k));
__m256i const deletion_source_vec = _mm256_loadu_si256(
reinterpret_cast<__m256i const *>(previous_row + column * candidate_lanes_k));
__m256i const insertion_source_vec = _mm256_loadu_si256(
reinterpret_cast<__m256i const *>(current_row + (column - 1) * candidate_lanes_k));
__m256i const equal_low_vec = _mm256_cmpeq_epi32(query_rune_vec, candidate_runes_low_vec);
__m256i const equal_high_vec = _mm256_cmpeq_epi32(query_rune_vec, candidate_runes_high_vec);
__m256i const equal_packed_vec = _mm256_packs_epi32(equal_low_vec, equal_high_vec);
__m256i const equal_i16_vec = _mm256_permute4x64_epi64(equal_packed_vec, 0xD8);
__m256i const mismatch_addend_vec = _mm256_blendv_epi8(mismatch_vec, match_vec, equal_i16_vec);
__m256i const cost_if_substitution_vec = _mm256_add_epi16(diagonal_vec, mismatch_addend_vec);
__m256i const cost_if_deletion_vec = _mm256_add_epi16(deletion_source_vec, gap_vec);
__m256i const cost_if_insertion_vec = _mm256_add_epi16(insertion_source_vec, gap_vec);
__m256i const cell_score_vec = _mm256_min_epu16(
cost_if_substitution_vec, _mm256_min_epu16(cost_if_deletion_vec, cost_if_insertion_vec));
_mm256_storeu_si256(reinterpret_cast<__m256i *>(current_row + column * candidate_lanes_k),
cell_score_vec);
}
trivial_swap(previous_row, current_row);
}
for (size_t lane_index = 0; lane_index < candidates.lanes_count; ++lane_index) {
size_t const candidate_length = candidates.lengths[lane_index];
result_lanes[lane_index] = previous_row[candidate_length * candidate_lanes_k + lane_index];
}
return status_t::success_k;
}
};
template <sz_similarity_objective_t objective_>
struct candidate_lane_walker<rune_t, u32_t, uniform_substitution_costs_t, linear_gap_costs_t, objective_,
sz_similarity_global_k, sz_cap_haswell_k, 8, void> {
using char_t = rune_t;
using score_t = u32_t;
using substituter_t = uniform_substitution_costs_t;
using gap_costs_t = linear_gap_costs_t;
static constexpr sz_similarity_objective_t objective_k = objective_;
static constexpr sz_similarity_locality_t locality_k = sz_similarity_global_k;
static constexpr sz_capability_t capability_k = sz_cap_haswell_k;
static constexpr size_t candidate_lanes_k = 8;
static constexpr size_t capacity_k = (size_t)std::numeric_limits<score_t>::max();
static_assert(objective_ == sz_minimize_distance_k,
"The 32-bit rune candidate-lane kernel only implements distance minimization (Levenshtein).");
substituter_t substituter_ {};
linear_gap_costs_t gap_costs_ {};
candidate_lane_walker() noexcept {}
candidate_lane_walker(substituter_t subs, linear_gap_costs_t gaps) noexcept
: substituter_(subs), gap_costs_(gaps) {}
size_t scratch_space_needed(size_t longest_candidate, cpu_specs_t const &specs) const noexcept {
size_t const row_bytes = candidate_lanes_k * (longest_candidate + 1) * sizeof(score_t);
scratch_amount_t amount {specs.cache_line_width};
amount += row_bytes; amount += row_bytes; return amount;
}
status_t operator()(span<char_t const> query, candidate_lanes_block<char_t> candidates, score_t *result_lanes,
scratch_space_t scratch_space, cpu_specs_t const &specs) const noexcept {
sz_unused_(specs);
size_t const query_length = query.size();
size_t const longest_candidate = candidates.longest_candidate;
size_t const row_stride = candidate_lanes_k * (longest_candidate + 1);
score_t *previous_row = reinterpret_cast<score_t *>(scratch_space.data());
score_t *current_row = previous_row + row_stride;
score_t const match_cost = static_cast<score_t>(substituter_.match);
score_t const mismatch_cost = static_cast<score_t>(substituter_.mismatch);
score_t const gap_cost = static_cast<score_t>(gap_costs_.open_or_extend);
__m256i const match_vec = _mm256_set1_epi32(static_cast<int>(match_cost));
__m256i const mismatch_vec = _mm256_set1_epi32(static_cast<int>(mismatch_cost));
__m256i const gap_vec = _mm256_set1_epi32(static_cast<int>(gap_cost));
for (size_t column = 0; column <= longest_candidate; ++column)
_mm256_storeu_si256(reinterpret_cast<__m256i *>(previous_row + column * candidate_lanes_k),
_mm256_set1_epi32(static_cast<int>(static_cast<u32_t>(column * gap_cost))));
for (size_t query_position = 1; query_position <= query_length; ++query_position) {
__m256i const query_rune_vec = _mm256_set1_epi32(
static_cast<int>(static_cast<u32_t>(query[query_position - 1])));
_mm256_storeu_si256(reinterpret_cast<__m256i *>(current_row),
_mm256_set1_epi32(static_cast<int>(static_cast<u32_t>(query_position * gap_cost))));
for (size_t column = 1; column <= longest_candidate; ++column) {
__m256i const candidate_runes_vec = _mm256_loadu_si256(
reinterpret_cast<__m256i const *>(candidates.position(column - 1)));
__m256i const diagonal_vec = _mm256_loadu_si256(
reinterpret_cast<__m256i const *>(previous_row + (column - 1) * candidate_lanes_k));
__m256i const deletion_source_vec = _mm256_loadu_si256(
reinterpret_cast<__m256i const *>(previous_row + column * candidate_lanes_k));
__m256i const insertion_source_vec = _mm256_loadu_si256(
reinterpret_cast<__m256i const *>(current_row + (column - 1) * candidate_lanes_k));
__m256i const equal_i32_vec = _mm256_cmpeq_epi32(query_rune_vec, candidate_runes_vec);
__m256i const mismatch_addend_vec = _mm256_blendv_epi8(mismatch_vec, match_vec, equal_i32_vec);
__m256i const cost_if_substitution_vec = _mm256_add_epi32(diagonal_vec, mismatch_addend_vec);
__m256i const cost_if_deletion_vec = _mm256_add_epi32(deletion_source_vec, gap_vec);
__m256i const cost_if_insertion_vec = _mm256_add_epi32(insertion_source_vec, gap_vec);
__m256i const cell_score_vec = _mm256_min_epu32(
cost_if_substitution_vec, _mm256_min_epu32(cost_if_deletion_vec, cost_if_insertion_vec));
_mm256_storeu_si256(reinterpret_cast<__m256i *>(current_row + column * candidate_lanes_k),
cell_score_vec);
}
trivial_swap(previous_row, current_row);
}
for (size_t lane_index = 0; lane_index < candidates.lanes_count; ++lane_index) {
size_t const candidate_length = candidates.lengths[lane_index];
result_lanes[lane_index] = previous_row[candidate_length * candidate_lanes_k + lane_index];
}
return status_t::success_k;
}
};
template <sz_similarity_objective_t objective_>
struct candidate_lane_walker<rune_t, u16_t, uniform_substitution_costs_t, affine_gap_costs_t, objective_,
sz_similarity_global_k, sz_cap_haswell_k, 16, void> {
using char_t = rune_t;
using score_t = u16_t;
using substituter_t = uniform_substitution_costs_t;
using gap_costs_t = affine_gap_costs_t;
static constexpr sz_similarity_objective_t objective_k = objective_;
static constexpr sz_similarity_locality_t locality_k = sz_similarity_global_k;
static constexpr sz_capability_t capability_k = sz_cap_haswell_k;
static constexpr size_t candidate_lanes_k = 16;
static constexpr size_t capacity_k = (size_t)std::numeric_limits<score_t>::max();
static constexpr size_t runes_per_vec_k = 8;
static_assert(objective_ == sz_minimize_distance_k,
"The 16-bit affine rune candidate-lane kernel only implements distance minimization (Levenshtein).");
substituter_t substituter_ {};
affine_gap_costs_t gap_costs_ {};
candidate_lane_walker() noexcept {}
candidate_lane_walker(substituter_t subs, affine_gap_costs_t gaps) noexcept
: substituter_(subs), gap_costs_(gaps) {}
size_t scratch_space_needed(size_t longest_candidate, cpu_specs_t const &specs) const noexcept {
size_t const score_row_bytes = candidate_lanes_k * (longest_candidate + 1) * sizeof(score_t);
scratch_amount_t amount {specs.cache_line_width};
amount += score_row_bytes; amount += score_row_bytes; amount += score_row_bytes; return amount;
}
status_t operator()(span<char_t const> query, candidate_lanes_block<char_t> candidates, score_t *result_lanes,
scratch_space_t scratch_space, cpu_specs_t const &specs) const noexcept {
sz_unused_(specs);
size_t const query_length = query.size();
size_t const longest_candidate = candidates.longest_candidate;
size_t const row_stride = candidate_lanes_k * (longest_candidate + 1);
score_t *previous_row = reinterpret_cast<score_t *>(scratch_space.data());
score_t *current_row = previous_row + row_stride;
score_t *vertical_row = current_row + row_stride;
score_t const match_cost = static_cast<score_t>(substituter_.match);
score_t const mismatch_cost = static_cast<score_t>(substituter_.mismatch);
score_t const open = static_cast<score_t>(gap_costs_.open);
score_t const extend = static_cast<score_t>(gap_costs_.extend);
__m256i const match_vec = _mm256_set1_epi16(static_cast<short>(match_cost));
__m256i const mismatch_vec = _mm256_set1_epi16(static_cast<short>(mismatch_cost));
__m256i const open_vec = _mm256_set1_epi16(static_cast<short>(open));
__m256i const extend_vec = _mm256_set1_epi16(static_cast<short>(extend));
__m256i const discard_bias_vec = _mm256_set1_epi16(static_cast<short>(static_cast<u16_t>(open + extend)));
_mm256_storeu_si256(reinterpret_cast<__m256i *>(previous_row), _mm256_setzero_si256());
_mm256_storeu_si256(reinterpret_cast<__m256i *>(vertical_row), discard_bias_vec);
for (size_t column = 1; column <= longest_candidate; ++column) {
__m256i const boundary_vec = _mm256_set1_epi16(
static_cast<short>(static_cast<u16_t>(open + extend * (u16_t)(column - 1))));
_mm256_storeu_si256(reinterpret_cast<__m256i *>(previous_row + column * candidate_lanes_k), boundary_vec);
_mm256_storeu_si256(reinterpret_cast<__m256i *>(vertical_row + column * candidate_lanes_k),
_mm256_add_epi16(discard_bias_vec, boundary_vec));
}
for (size_t query_position = 1; query_position <= query_length; ++query_position) {
__m256i const query_rune_vec = _mm256_set1_epi32(
static_cast<int>(static_cast<u32_t>(query[query_position - 1])));
__m256i const left_boundary_vec = _mm256_set1_epi16(
static_cast<short>(static_cast<u16_t>(open + extend * (u16_t)(query_position - 1))));
_mm256_storeu_si256(reinterpret_cast<__m256i *>(current_row), left_boundary_vec);
__m256i horizontal_vec = _mm256_add_epi16(discard_bias_vec, left_boundary_vec);
for (size_t column = 1; column <= longest_candidate; ++column) {
__m256i const candidate_runes_low_vec = _mm256_loadu_si256(
reinterpret_cast<__m256i const *>(candidates.position(column - 1)));
__m256i const candidate_runes_high_vec = _mm256_loadu_si256(
reinterpret_cast<__m256i const *>(candidates.position(column - 1) + runes_per_vec_k));
__m256i const diagonal_vec = _mm256_loadu_si256(
reinterpret_cast<__m256i const *>(previous_row + (column - 1) * candidate_lanes_k));
__m256i const up_vec = _mm256_loadu_si256(
reinterpret_cast<__m256i const *>(previous_row + column * candidate_lanes_k));
__m256i const left_vec = _mm256_loadu_si256(
reinterpret_cast<__m256i const *>(current_row + (column - 1) * candidate_lanes_k));
__m256i const up_vertical_vec = _mm256_loadu_si256(
reinterpret_cast<__m256i const *>(vertical_row + column * candidate_lanes_k));
__m256i const equal_low_vec = _mm256_cmpeq_epi32(query_rune_vec, candidate_runes_low_vec);
__m256i const equal_high_vec = _mm256_cmpeq_epi32(query_rune_vec, candidate_runes_high_vec);
__m256i const equal_packed_vec = _mm256_packs_epi32(equal_low_vec, equal_high_vec);
__m256i const equal_i16_vec = _mm256_permute4x64_epi64(equal_packed_vec, 0xD8);
__m256i const substitution_addend_vec = _mm256_blendv_epi8(mismatch_vec, match_vec, equal_i16_vec);
__m256i const cost_if_substitution_vec = _mm256_add_epi16(diagonal_vec, substitution_addend_vec);
__m256i const vertical_vec = _mm256_min_epu16(_mm256_add_epi16(up_vec, open_vec),
_mm256_add_epi16(up_vertical_vec, extend_vec));
horizontal_vec = _mm256_min_epu16(_mm256_add_epi16(left_vec, open_vec),
_mm256_add_epi16(horizontal_vec, extend_vec));
__m256i const cost_if_gap_vec = _mm256_min_epu16(vertical_vec, horizontal_vec);
__m256i const cell_score_vec = _mm256_min_epu16(cost_if_substitution_vec, cost_if_gap_vec);
_mm256_storeu_si256(reinterpret_cast<__m256i *>(vertical_row + column * candidate_lanes_k),
vertical_vec);
_mm256_storeu_si256(reinterpret_cast<__m256i *>(current_row + column * candidate_lanes_k),
cell_score_vec);
}
trivial_swap(previous_row, current_row);
}
for (size_t lane_index = 0; lane_index < candidates.lanes_count; ++lane_index) {
size_t const candidate_length = candidates.lengths[lane_index];
result_lanes[lane_index] = previous_row[candidate_length * candidate_lanes_k + lane_index];
}
return status_t::success_k;
}
};
template <sz_similarity_objective_t objective_>
struct candidate_lane_walker<rune_t, u32_t, uniform_substitution_costs_t, affine_gap_costs_t, objective_,
sz_similarity_global_k, sz_cap_haswell_k, 8, void> {
using char_t = rune_t;
using score_t = u32_t;
using substituter_t = uniform_substitution_costs_t;
using gap_costs_t = affine_gap_costs_t;
static constexpr sz_similarity_objective_t objective_k = objective_;
static constexpr sz_similarity_locality_t locality_k = sz_similarity_global_k;
static constexpr sz_capability_t capability_k = sz_cap_haswell_k;
static constexpr size_t candidate_lanes_k = 8;
static constexpr size_t capacity_k = (size_t)std::numeric_limits<score_t>::max();
static_assert(objective_ == sz_minimize_distance_k,
"The 32-bit affine rune candidate-lane kernel only implements distance minimization (Levenshtein).");
substituter_t substituter_ {};
affine_gap_costs_t gap_costs_ {};
candidate_lane_walker() noexcept {}
candidate_lane_walker(substituter_t subs, affine_gap_costs_t gaps) noexcept
: substituter_(subs), gap_costs_(gaps) {}
size_t scratch_space_needed(size_t longest_candidate, cpu_specs_t const &specs) const noexcept {
size_t const score_row_bytes = candidate_lanes_k * (longest_candidate + 1) * sizeof(score_t);
scratch_amount_t amount {specs.cache_line_width};
amount += score_row_bytes; amount += score_row_bytes; amount += score_row_bytes; return amount;
}
status_t operator()(span<char_t const> query, candidate_lanes_block<char_t> candidates, score_t *result_lanes,
scratch_space_t scratch_space, cpu_specs_t const &specs) const noexcept {
sz_unused_(specs);
size_t const query_length = query.size();
size_t const longest_candidate = candidates.longest_candidate;
size_t const row_stride = candidate_lanes_k * (longest_candidate + 1);
score_t *previous_row = reinterpret_cast<score_t *>(scratch_space.data());
score_t *current_row = previous_row + row_stride;
score_t *vertical_row = current_row + row_stride;
score_t const match_cost = static_cast<score_t>(substituter_.match);
score_t const mismatch_cost = static_cast<score_t>(substituter_.mismatch);
score_t const open = static_cast<score_t>(gap_costs_.open);
score_t const extend = static_cast<score_t>(gap_costs_.extend);
__m256i const match_vec = _mm256_set1_epi32(static_cast<int>(match_cost));
__m256i const mismatch_vec = _mm256_set1_epi32(static_cast<int>(mismatch_cost));
__m256i const open_vec = _mm256_set1_epi32(static_cast<int>(open));
__m256i const extend_vec = _mm256_set1_epi32(static_cast<int>(extend));
__m256i const discard_bias_vec = _mm256_set1_epi32(static_cast<int>(static_cast<u32_t>(open + extend)));
_mm256_storeu_si256(reinterpret_cast<__m256i *>(previous_row), _mm256_setzero_si256());
_mm256_storeu_si256(reinterpret_cast<__m256i *>(vertical_row), discard_bias_vec);
for (size_t column = 1; column <= longest_candidate; ++column) {
__m256i const boundary_vec = _mm256_set1_epi32(
static_cast<int>(static_cast<u32_t>(open + extend * (u32_t)(column - 1))));
_mm256_storeu_si256(reinterpret_cast<__m256i *>(previous_row + column * candidate_lanes_k), boundary_vec);
_mm256_storeu_si256(reinterpret_cast<__m256i *>(vertical_row + column * candidate_lanes_k),
_mm256_add_epi32(discard_bias_vec, boundary_vec));
}
for (size_t query_position = 1; query_position <= query_length; ++query_position) {
__m256i const query_rune_vec = _mm256_set1_epi32(
static_cast<int>(static_cast<u32_t>(query[query_position - 1])));
__m256i const left_boundary_vec = _mm256_set1_epi32(
static_cast<int>(static_cast<u32_t>(open + extend * (u32_t)(query_position - 1))));
_mm256_storeu_si256(reinterpret_cast<__m256i *>(current_row), left_boundary_vec);
__m256i horizontal_vec = _mm256_add_epi32(discard_bias_vec, left_boundary_vec);
for (size_t column = 1; column <= longest_candidate; ++column) {
__m256i const candidate_runes_vec = _mm256_loadu_si256(
reinterpret_cast<__m256i const *>(candidates.position(column - 1)));
__m256i const diagonal_vec = _mm256_loadu_si256(
reinterpret_cast<__m256i const *>(previous_row + (column - 1) * candidate_lanes_k));
__m256i const up_vec = _mm256_loadu_si256(
reinterpret_cast<__m256i const *>(previous_row + column * candidate_lanes_k));
__m256i const left_vec = _mm256_loadu_si256(
reinterpret_cast<__m256i const *>(current_row + (column - 1) * candidate_lanes_k));
__m256i const up_vertical_vec = _mm256_loadu_si256(
reinterpret_cast<__m256i const *>(vertical_row + column * candidate_lanes_k));
__m256i const equal_i32_vec = _mm256_cmpeq_epi32(query_rune_vec, candidate_runes_vec);
__m256i const substitution_addend_vec = _mm256_blendv_epi8(mismatch_vec, match_vec, equal_i32_vec);
__m256i const cost_if_substitution_vec = _mm256_add_epi32(diagonal_vec, substitution_addend_vec);
__m256i const vertical_vec = _mm256_min_epu32(_mm256_add_epi32(up_vec, open_vec),
_mm256_add_epi32(up_vertical_vec, extend_vec));
horizontal_vec = _mm256_min_epu32(_mm256_add_epi32(left_vec, open_vec),
_mm256_add_epi32(horizontal_vec, extend_vec));
__m256i const cost_if_gap_vec = _mm256_min_epu32(vertical_vec, horizontal_vec);
__m256i const cell_score_vec = _mm256_min_epu32(cost_if_substitution_vec, cost_if_gap_vec);
_mm256_storeu_si256(reinterpret_cast<__m256i *>(vertical_row + column * candidate_lanes_k),
vertical_vec);
_mm256_storeu_si256(reinterpret_cast<__m256i *>(current_row + column * candidate_lanes_k),
cell_score_vec);
}
trivial_swap(previous_row, current_row);
}
for (size_t lane_index = 0; lane_index < candidates.lanes_count; ++lane_index) {
size_t const candidate_length = candidates.lengths[lane_index];
result_lanes[lane_index] = previous_row[candidate_length * candidate_lanes_k + lane_index];
}
return status_t::success_k;
}
};
template <sz_similarity_objective_t objective_, typename gap_costs_type_, sz_similarity_locality_t locality_>
struct candidate_lane_walker<char, i16_t, error_costs_32x32_t, gap_costs_type_, objective_, locality_, sz_cap_haswell_k,
16, void> {
using char_t = char;
using score_t = i16_t;
using substituter_t = error_costs_32x32_t;
using gap_costs_t = gap_costs_type_;
static constexpr sz_similarity_objective_t objective_k = objective_;
static constexpr sz_similarity_locality_t locality_k = locality_;
static constexpr sz_capability_t capability_k = sz_cap_haswell_k;
static constexpr size_t candidate_lanes_k = 16;
static constexpr size_t capacity_k = (size_t)std::numeric_limits<score_t>::max();
static constexpr bool is_affine_k = is_same_type<gap_costs_type_, affine_gap_costs_t>::value;
static constexpr bool is_local_k = locality_ == sz_similarity_local_k;
static_assert(
objective_ == sz_maximize_score_k,
"The weighted candidate-lane kernel only implements score " "maximization (Needleman-Wunsch / " "Smith-" "Water" "man)" ".");
substituter_t substituter_ {};
gap_costs_type_ gap_costs_ {};
candidate_lane_walker() noexcept {}
candidate_lane_walker(substituter_t subs, gap_costs_type_ gaps) noexcept : substituter_(subs), gap_costs_(gaps) {}
size_t scratch_space_needed(size_t longest_candidate, cpu_specs_t const &specs) const noexcept {
size_t const score_row_bytes = candidate_lanes_k * (longest_candidate + 1) * sizeof(score_t);
size_t const class_bytes = candidate_lanes_k * longest_candidate * sizeof(u8_t);
scratch_amount_t amount {specs.cache_line_width};
amount += score_row_bytes; amount += score_row_bytes; if constexpr (is_affine_k) amount += score_row_bytes; amount += class_bytes; return amount;
}
status_t operator()(span<char_t const> query, candidate_lanes_block<char_t> candidates, score_t *result_lanes,
scratch_space_t scratch_space, cpu_specs_t const &specs) const noexcept {
sz_unused_(specs);
size_t const query_length = query.size();
size_t const longest_candidate = candidates.longest_candidate;
size_t const row_stride = candidate_lanes_k * (longest_candidate + 1);
score_t *previous_row = reinterpret_cast<score_t *>(scratch_space.data());
score_t *current_row = previous_row + row_stride;
class_lookup_haswell_t lookup;
lookup.reload_classes(substituter_.byte_to_class);
__m256i const zero_vec = _mm256_setzero_si256();
[[maybe_unused]] error_cost_t gap = 0, open = 0, extend = 0;
[[maybe_unused]] __m256i gap_vec = zero_vec, open_vec = zero_vec, extend_vec = zero_vec,
discard_bias_vec = zero_vec;
[[maybe_unused]] score_t *vertical_row = nullptr;
u8_t *candidate_classes = nullptr;
if constexpr (is_affine_k) {
open = gap_costs_.open;
extend = gap_costs_.extend;
open_vec = _mm256_set1_epi16(static_cast<short>(open));
extend_vec = _mm256_set1_epi16(static_cast<short>(extend));
discard_bias_vec = _mm256_set1_epi16(static_cast<short>(open + extend));
vertical_row = current_row + row_stride;
candidate_classes = reinterpret_cast<u8_t *>(vertical_row + row_stride);
}
else {
gap = gap_costs_.open_or_extend;
gap_vec = _mm256_set1_epi16(static_cast<short>(gap));
candidate_classes = reinterpret_cast<u8_t *>(current_row + row_stride);
}
for (size_t column = 0; column < longest_candidate; ++column) {
u256_vec_t candidate_chars_vec;
candidate_chars_vec.ymm = _mm256_castsi128_si256(
_mm_loadu_si128(reinterpret_cast<__m128i const *>(candidates.position(column))));
u256_vec_t const candidate_classes_vec = lookup.classify32(candidate_chars_vec);
_mm_storeu_si128(reinterpret_cast<__m128i *>(candidate_classes + column * candidate_lanes_k),
_mm256_castsi256_si128(candidate_classes_vec.ymm));
}
[[maybe_unused]] alignas(32) i16_t lane_lengths[candidate_lanes_k] = {0};
[[maybe_unused]] __m256i lane_lengths_vec = zero_vec;
[[maybe_unused]] __m256i running_max_vec = zero_vec;
if constexpr (is_local_k) {
for (size_t lane_index = 0; lane_index < candidates.lanes_count; ++lane_index)
lane_lengths[lane_index] = static_cast<i16_t>(candidates.lengths[lane_index]);
lane_lengths_vec = _mm256_load_si256(reinterpret_cast<__m256i const *>(lane_lengths));
}
if constexpr (is_local_k) {
for (size_t column = 0; column <= longest_candidate; ++column) {
_mm256_storeu_si256(reinterpret_cast<__m256i *>(previous_row + column * candidate_lanes_k), zero_vec);
if constexpr (is_affine_k)
_mm256_storeu_si256(reinterpret_cast<__m256i *>(vertical_row + column * candidate_lanes_k),
discard_bias_vec);
}
}
else if constexpr (is_affine_k) {
_mm256_storeu_si256(reinterpret_cast<__m256i *>(previous_row), zero_vec);
_mm256_storeu_si256(reinterpret_cast<__m256i *>(vertical_row),
_mm256_add_epi16(discard_bias_vec, zero_vec));
for (size_t column = 1; column <= longest_candidate; ++column) {
__m256i const boundary_vec = _mm256_set1_epi16(
static_cast<short>(static_cast<i16_t>(open + extend * (i16_t)(column - 1))));
_mm256_storeu_si256(reinterpret_cast<__m256i *>(previous_row + column * candidate_lanes_k),
boundary_vec);
_mm256_storeu_si256(reinterpret_cast<__m256i *>(vertical_row + column * candidate_lanes_k),
_mm256_add_epi16(discard_bias_vec, boundary_vec));
}
}
else {
for (size_t column = 0; column <= longest_candidate; ++column)
_mm256_storeu_si256(reinterpret_cast<__m256i *>(previous_row + column * candidate_lanes_k),
_mm256_set1_epi16(static_cast<short>(static_cast<i16_t>(gap * (i16_t)column))));
}
for (size_t query_position = 1; query_position <= query_length; ++query_position) {
u8_t const query_class = substituter_.byte_to_class[(u8_t)query[query_position - 1]];
lookup.reload_row(&substituter_.class_substitution_costs[query_class][0]);
[[maybe_unused]] __m256i horizontal_vec = zero_vec;
if constexpr (is_local_k) { _mm256_storeu_si256(reinterpret_cast<__m256i *>(current_row), zero_vec); }
else if constexpr (is_affine_k) {
__m256i const left_boundary_vec = _mm256_set1_epi16(
static_cast<short>(static_cast<i16_t>(open + extend * (i16_t)(query_position - 1))));
_mm256_storeu_si256(reinterpret_cast<__m256i *>(current_row), left_boundary_vec);
horizontal_vec = _mm256_add_epi16(discard_bias_vec, left_boundary_vec);
}
else {
_mm256_storeu_si256(
reinterpret_cast<__m256i *>(current_row),
_mm256_set1_epi16(static_cast<short>(static_cast<i16_t>(gap * (i16_t)query_position))));
}
if constexpr (is_local_k && is_affine_k) horizontal_vec = discard_bias_vec;
for (size_t column = 1; column <= longest_candidate; ++column) {
u256_vec_t candidate_classes_vec;
candidate_classes_vec.ymm = _mm256_castsi128_si256(_mm_loadu_si128(
reinterpret_cast<__m128i const *>(candidate_classes + (column - 1) * candidate_lanes_k)));
__m256i const diagonal_vec = _mm256_loadu_si256(
reinterpret_cast<__m256i const *>(previous_row + (column - 1) * candidate_lanes_k));
__m256i const up_vec = _mm256_loadu_si256(
reinterpret_cast<__m256i const *>(previous_row + column * candidate_lanes_k));
__m256i const left_vec = _mm256_loadu_si256(
reinterpret_cast<__m256i const *>(current_row + (column - 1) * candidate_lanes_k));
u256_vec_t const cost_i8_vec = lookup.costs_for_classes32(candidate_classes_vec);
__m256i const cost_i16_vec = _mm256_cvtepi8_epi16(_mm256_castsi256_si128(cost_i8_vec.ymm));
__m256i const cost_if_substitution_vec = _mm256_add_epi16(diagonal_vec, cost_i16_vec);
__m256i cost_if_gap_vec;
if constexpr (is_affine_k) {
__m256i const up_vertical_vec = _mm256_loadu_si256(
reinterpret_cast<__m256i const *>(vertical_row + column * candidate_lanes_k));
__m256i const vertical_vec = _mm256_max_epi16(_mm256_add_epi16(up_vec, open_vec),
_mm256_add_epi16(up_vertical_vec, extend_vec));
horizontal_vec = _mm256_max_epi16(_mm256_add_epi16(left_vec, open_vec),
_mm256_add_epi16(horizontal_vec, extend_vec));
cost_if_gap_vec = _mm256_max_epi16(vertical_vec, horizontal_vec);
_mm256_storeu_si256(reinterpret_cast<__m256i *>(vertical_row + column * candidate_lanes_k),
vertical_vec);
}
else { cost_if_gap_vec = _mm256_add_epi16(_mm256_max_epi16(up_vec, left_vec), gap_vec); }
__m256i cell_score_vec = _mm256_max_epi16(cost_if_substitution_vec, cost_if_gap_vec);
if constexpr (is_local_k) cell_score_vec = _mm256_max_epi16(zero_vec, cell_score_vec);
_mm256_storeu_si256(reinterpret_cast<__m256i *>(current_row + column * candidate_lanes_k),
cell_score_vec);
if constexpr (is_local_k) {
__m256i const column_live_vec = _mm256_cmpgt_epi16(
lane_lengths_vec, _mm256_set1_epi16(static_cast<short>(column - 1)));
__m256i const folded_max_vec = _mm256_max_epi16(running_max_vec, cell_score_vec);
running_max_vec = _mm256_blendv_epi8(running_max_vec, folded_max_vec, column_live_vec);
}
}
trivial_swap(previous_row, current_row);
}
if constexpr (is_local_k) {
alignas(32) i16_t final_max[candidate_lanes_k];
_mm256_store_si256(reinterpret_cast<__m256i *>(final_max), running_max_vec);
for (size_t lane_index = 0; lane_index < candidates.lanes_count; ++lane_index)
result_lanes[lane_index] = final_max[lane_index];
}
else {
for (size_t lane_index = 0; lane_index < candidates.lanes_count; ++lane_index) {
size_t const candidate_length = candidates.lengths[lane_index];
result_lanes[lane_index] = previous_row[candidate_length * candidate_lanes_k + lane_index];
}
}
return status_t::success_k;
}
};
template <sz_similarity_objective_t objective_, typename gap_costs_type_, sz_similarity_locality_t locality_>
struct candidate_lane_walker<char, i32_t, error_costs_32x32_t, gap_costs_type_, objective_, locality_, sz_cap_haswell_k,
8, void> {
using char_t = char;
using score_t = i32_t;
using substituter_t = error_costs_32x32_t;
using gap_costs_t = gap_costs_type_;
static constexpr sz_similarity_objective_t objective_k = objective_;
static constexpr sz_similarity_locality_t locality_k = locality_;
static constexpr sz_capability_t capability_k = sz_cap_haswell_k;
static constexpr size_t candidate_lanes_k = 8;
static constexpr size_t capacity_k = (size_t)std::numeric_limits<score_t>::max();
static constexpr bool is_affine_k = is_same_type<gap_costs_type_, affine_gap_costs_t>::value;
static constexpr bool is_local_k = locality_ == sz_similarity_local_k;
static_assert(
objective_ == sz_maximize_score_k,
"The weighted candidate-lane kernel only implements score " "maximization (Needleman-Wunsch / " "Smith-" "Water" "man)" ".");
substituter_t substituter_ {};
gap_costs_type_ gap_costs_ {};
candidate_lane_walker() noexcept {}
candidate_lane_walker(substituter_t subs, gap_costs_type_ gaps) noexcept : substituter_(subs), gap_costs_(gaps) {}
size_t scratch_space_needed(size_t longest_candidate, cpu_specs_t const &specs) const noexcept {
size_t const score_row_bytes = candidate_lanes_k * (longest_candidate + 1) * sizeof(score_t);
size_t const class_bytes = candidate_lanes_k * longest_candidate * sizeof(u8_t);
scratch_amount_t amount {specs.cache_line_width};
amount += score_row_bytes; amount += score_row_bytes; if constexpr (is_affine_k) amount += score_row_bytes; amount += class_bytes; return amount;
}
status_t operator()(span<char_t const> query, candidate_lanes_block<char_t> candidates, score_t *result_lanes,
scratch_space_t scratch_space, cpu_specs_t const &specs) const noexcept {
sz_unused_(specs);
size_t const query_length = query.size();
size_t const longest_candidate = candidates.longest_candidate;
size_t const row_stride = candidate_lanes_k * (longest_candidate + 1);
score_t *previous_row = reinterpret_cast<score_t *>(scratch_space.data());
score_t *current_row = previous_row + row_stride;
class_lookup_haswell_t lookup;
lookup.reload_classes(substituter_.byte_to_class);
__m256i const zero_vec = _mm256_setzero_si256();
[[maybe_unused]] error_cost_t gap = 0, open = 0, extend = 0;
[[maybe_unused]] __m256i gap_vec = zero_vec, open_vec = zero_vec, extend_vec = zero_vec,
discard_bias_vec = zero_vec;
[[maybe_unused]] score_t *vertical_row = nullptr;
u8_t *candidate_classes = nullptr;
if constexpr (is_affine_k) {
open = gap_costs_.open;
extend = gap_costs_.extend;
open_vec = _mm256_set1_epi32(static_cast<int>(open));
extend_vec = _mm256_set1_epi32(static_cast<int>(extend));
discard_bias_vec = _mm256_set1_epi32(static_cast<int>(open + extend));
vertical_row = current_row + row_stride;
candidate_classes = reinterpret_cast<u8_t *>(vertical_row + row_stride);
}
else {
gap = gap_costs_.open_or_extend;
gap_vec = _mm256_set1_epi32(static_cast<int>(gap));
candidate_classes = reinterpret_cast<u8_t *>(current_row + row_stride);
}
for (size_t column = 0; column < longest_candidate; ++column) {
u256_vec_t candidate_chars_vec;
candidate_chars_vec.ymm = _mm256_castsi128_si256(
_mm_loadl_epi64(reinterpret_cast<__m128i const *>(candidates.position(column))));
u256_vec_t const candidate_classes_vec = lookup.classify32(candidate_chars_vec);
_mm_storel_epi64(reinterpret_cast<__m128i *>(candidate_classes + column * candidate_lanes_k),
_mm256_castsi256_si128(candidate_classes_vec.ymm));
}
[[maybe_unused]] alignas(32) i32_t lane_lengths[candidate_lanes_k] = {0};
[[maybe_unused]] __m256i lane_lengths_vec = zero_vec;
[[maybe_unused]] __m256i running_max_vec = zero_vec;
if constexpr (is_local_k) {
for (size_t lane_index = 0; lane_index < candidates.lanes_count; ++lane_index)
lane_lengths[lane_index] = static_cast<i32_t>(candidates.lengths[lane_index]);
lane_lengths_vec = _mm256_load_si256(reinterpret_cast<__m256i const *>(lane_lengths));
}
if constexpr (is_local_k) {
for (size_t column = 0; column <= longest_candidate; ++column) {
_mm256_storeu_si256(reinterpret_cast<__m256i *>(previous_row + column * candidate_lanes_k), zero_vec);
if constexpr (is_affine_k)
_mm256_storeu_si256(reinterpret_cast<__m256i *>(vertical_row + column * candidate_lanes_k),
discard_bias_vec);
}
}
else if constexpr (is_affine_k) {
_mm256_storeu_si256(reinterpret_cast<__m256i *>(previous_row), zero_vec);
_mm256_storeu_si256(reinterpret_cast<__m256i *>(vertical_row),
_mm256_add_epi32(discard_bias_vec, zero_vec));
for (size_t column = 1; column <= longest_candidate; ++column) {
__m256i const boundary_vec = _mm256_set1_epi32(
static_cast<int>(static_cast<i32_t>(open + extend * (i32_t)(column - 1))));
_mm256_storeu_si256(reinterpret_cast<__m256i *>(previous_row + column * candidate_lanes_k),
boundary_vec);
_mm256_storeu_si256(reinterpret_cast<__m256i *>(vertical_row + column * candidate_lanes_k),
_mm256_add_epi32(discard_bias_vec, boundary_vec));
}
}
else {
for (size_t column = 0; column <= longest_candidate; ++column)
_mm256_storeu_si256(reinterpret_cast<__m256i *>(previous_row + column * candidate_lanes_k),
_mm256_set1_epi32(static_cast<int>(static_cast<i32_t>(gap * (i32_t)column))));
}
for (size_t query_position = 1; query_position <= query_length; ++query_position) {
u8_t const query_class = substituter_.byte_to_class[(u8_t)query[query_position - 1]];
lookup.reload_row(&substituter_.class_substitution_costs[query_class][0]);
[[maybe_unused]] __m256i horizontal_vec = zero_vec;
if constexpr (is_local_k) { _mm256_storeu_si256(reinterpret_cast<__m256i *>(current_row), zero_vec); }
else if constexpr (is_affine_k) {
__m256i const left_boundary_vec = _mm256_set1_epi32(
static_cast<int>(static_cast<i32_t>(open + extend * (i32_t)(query_position - 1))));
_mm256_storeu_si256(reinterpret_cast<__m256i *>(current_row), left_boundary_vec);
horizontal_vec = _mm256_add_epi32(discard_bias_vec, left_boundary_vec);
}
else {
_mm256_storeu_si256(
reinterpret_cast<__m256i *>(current_row),
_mm256_set1_epi32(static_cast<int>(static_cast<i32_t>(gap * (i32_t)query_position))));
}
if constexpr (is_local_k && is_affine_k) horizontal_vec = discard_bias_vec;
for (size_t column = 1; column <= longest_candidate; ++column) {
u256_vec_t candidate_classes_vec;
candidate_classes_vec.ymm = _mm256_castsi128_si256(_mm_loadl_epi64(
reinterpret_cast<__m128i const *>(candidate_classes + (column - 1) * candidate_lanes_k)));
__m256i const diagonal_vec = _mm256_loadu_si256(
reinterpret_cast<__m256i const *>(previous_row + (column - 1) * candidate_lanes_k));
__m256i const up_vec = _mm256_loadu_si256(
reinterpret_cast<__m256i const *>(previous_row + column * candidate_lanes_k));
__m256i const left_vec = _mm256_loadu_si256(
reinterpret_cast<__m256i const *>(current_row + (column - 1) * candidate_lanes_k));
u256_vec_t const cost_i8_vec = lookup.costs_for_classes32(candidate_classes_vec);
__m256i const cost_i32_vec = _mm256_cvtepi8_epi32(_mm256_castsi256_si128(cost_i8_vec.ymm));
__m256i const cost_if_substitution_vec = _mm256_add_epi32(diagonal_vec, cost_i32_vec);
__m256i cost_if_gap_vec;
if constexpr (is_affine_k) {
__m256i const up_vertical_vec = _mm256_loadu_si256(
reinterpret_cast<__m256i const *>(vertical_row + column * candidate_lanes_k));
__m256i const vertical_vec = _mm256_max_epi32(_mm256_add_epi32(up_vec, open_vec),
_mm256_add_epi32(up_vertical_vec, extend_vec));
horizontal_vec = _mm256_max_epi32(_mm256_add_epi32(left_vec, open_vec),
_mm256_add_epi32(horizontal_vec, extend_vec));
cost_if_gap_vec = _mm256_max_epi32(vertical_vec, horizontal_vec);
_mm256_storeu_si256(reinterpret_cast<__m256i *>(vertical_row + column * candidate_lanes_k),
vertical_vec);
}
else { cost_if_gap_vec = _mm256_add_epi32(_mm256_max_epi32(up_vec, left_vec), gap_vec); }
__m256i cell_score_vec = _mm256_max_epi32(cost_if_substitution_vec, cost_if_gap_vec);
if constexpr (is_local_k) cell_score_vec = _mm256_max_epi32(zero_vec, cell_score_vec);
_mm256_storeu_si256(reinterpret_cast<__m256i *>(current_row + column * candidate_lanes_k),
cell_score_vec);
if constexpr (is_local_k) {
__m256i const column_live_vec = _mm256_cmpgt_epi32(lane_lengths_vec,
_mm256_set1_epi32(static_cast<int>(column - 1)));
__m256i const folded_max_vec = _mm256_max_epi32(running_max_vec, cell_score_vec);
running_max_vec = _mm256_blendv_epi8(running_max_vec, folded_max_vec, column_live_vec);
}
}
trivial_swap(previous_row, current_row);
}
if constexpr (is_local_k) {
alignas(32) i32_t final_max[candidate_lanes_k];
_mm256_store_si256(reinterpret_cast<__m256i *>(final_max), running_max_vec);
for (size_t lane_index = 0; lane_index < candidates.lanes_count; ++lane_index)
result_lanes[lane_index] = final_max[lane_index];
}
else {
for (size_t lane_index = 0; lane_index < candidates.lanes_count; ++lane_index) {
size_t const candidate_length = candidates.lengths[lane_index];
result_lanes[lane_index] = previous_row[candidate_length * candidate_lanes_k + lane_index];
}
}
return status_t::success_k;
}
};
#pragma endregion Inter Sequence Candidate Lanes
#pragma region Inter Sequence Byte Myers
template <sz_capability_t capability_>
struct levenshtein_distance_myers<
char, capability_,
std::enable_if_t<(capability_ & sz_cap_haswell_k) != 0 && (capability_ & sz_cap_icelake_k) == 0>> {
using char_t = char;
using index_t = u32_t;
static constexpr index_t lanes_k = 4;
static constexpr size_t match_masks_bytes_k = sizeof(u64_t) * lanes_k * 256;
levenshtein_distance_myers() noexcept {}
auto layout(span<char_t const> first, span<char_t const> second, cpu_specs_t const &specs) const noexcept {
return levenshtein_distance_myers<char, sz_cap_serial_k> {}.layout(first, second, specs);
}
status_t operator()(span<char_t const> const &first, span<char_t const> const &second, size_t &result_ref,
scratch_space_t scratch_space) noexcept {
return levenshtein_distance_myers<char, sz_cap_serial_k> {}(first, second, result_ref, scratch_space);
}
static __m256i lane_test_(__m256i value, __m256i probe, __m256i zero) noexcept {
__m256i const masked = _mm256_and_si256(value, probe);
return _mm256_andnot_si256(_mm256_cmpeq_epi64(masked, zero), _mm256_set1_epi64x(-1));
}
static __m256i lane_less_unsigned_(__m256i first, __m256i second, __m256i sign_bit) noexcept {
return _mm256_cmpgt_epi64(_mm256_xor_si256(second, sign_bit), _mm256_xor_si256(first, sign_bit));
}
static __m256i or_nor_(__m256i first, __m256i second, __m256i third, __m256i ones) noexcept {
return _mm256_or_si256(first, _mm256_andnot_si256(_mm256_or_si256(second, third), ones));
}
template <typename results_writer_>
status_t distances_4x64_(lane_pairs_view<char_t> const &pairs, results_writer_ &results,
scratch_space_t scratch_space) const noexcept {
size_t max_longer = 0;
for (index_t lane_index = 0; lane_index != pairs.lanes_count(); ++lane_index)
max_longer = sz_max_of_two(max_longer, pairs.longers[lane_index].size());
if (scratch_space.size() < match_masks_bytes_k + max_longer * lanes_k) return status_t::bad_alloc_k;
u64_t *const match_masks = reinterpret_cast<u64_t *>(scratch_space.data()); u8_t *const transposed_text = reinterpret_cast<u8_t *>(scratch_space.data() + match_masks_bytes_k);
alignas(32) u64_t top_bits[lanes_k] = {0}, shorter_lengths[lanes_k] = {0}, longer_lengths[lanes_k] = {0};
for (size_t position = 0; position != max_longer * lanes_k; ++position) transposed_text[position] = 0;
for (index_t lane_index = 0; lane_index != pairs.lanes_count(); ++lane_index) {
index_t const shorter_length = (index_t)pairs.shorters[lane_index].size();
size_t const longer_length = pairs.longers[lane_index].size();
char_t const *const shorter = pairs.shorters[lane_index].data();
char_t const *const longer = pairs.longers[lane_index].data();
for (index_t position = 0; position != shorter_length; ++position)
match_masks[lane_index * 256 + (u8_t)shorter[position]] = 0;
for (size_t position = 0; position != longer_length; ++position)
match_masks[lane_index * 256 + (u8_t)longer[position]] = 0;
for (index_t position = 0; position != shorter_length; ++position)
match_masks[lane_index * 256 + (u8_t)shorter[position]] |= (u64_t)1 << position;
top_bits[lane_index] = (u64_t)1 << (shorter_length - 1);
shorter_lengths[lane_index] = shorter_length;
longer_lengths[lane_index] = longer_length;
for (size_t position = 0; position != longer_length; ++position)
transposed_text[position * lanes_k + lane_index] = (u8_t)longer[position];
}
__m256i const lane_offsets = _mm256_set_epi64x(3 * 256, 2 * 256, 1 * 256, 0);
__m256i const one = _mm256_set1_epi64x(1);
__m256i const ones = _mm256_set1_epi64x(-1);
__m256i const zero = _mm256_setzero_si256();
__m256i const top_mask = _mm256_load_si256((__m256i const *)top_bits);
__m256i const longer_vec = _mm256_load_si256((__m256i const *)longer_lengths);
__m256i const length_vec = _mm256_load_si256((__m256i const *)shorter_lengths);
__m256i vertical_positive = _mm256_sub_epi64(_mm256_sllv_epi64(one, length_vec), one);
__m256i vertical_negative = _mm256_setzero_si256();
__m256i score = length_vec;
for (size_t position = 0; position != max_longer; ++position) {
__m256i const active = _mm256_cmpgt_epi64(longer_vec, _mm256_set1_epi64x((long long)position));
__m256i const symbols = _mm256_cvtepu8_epi64(_mm_loadu_si32(transposed_text + position * lanes_k));
__m256i const equality = _mm256_i64gather_epi64((long long const *)match_masks,
_mm256_add_epi64(lane_offsets, symbols), 8);
__m256i const carry_in = _mm256_or_si256(equality, vertical_negative);
__m256i const sum = _mm256_add_epi64(_mm256_and_si256(equality, vertical_positive), vertical_positive);
__m256i const diagonal = _mm256_or_si256(_mm256_xor_si256(sum, vertical_positive), equality);
__m256i horizontal_positive = or_nor_(vertical_negative, diagonal, vertical_positive, ones); __m256i horizontal_negative = _mm256_and_si256(vertical_positive, diagonal); __m256i const add_mask = _mm256_and_si256(active, lane_test_(horizontal_positive, top_mask, zero));
__m256i const sub_mask = _mm256_and_si256(active, lane_test_(horizontal_negative, top_mask, zero));
score = _mm256_add_epi64(score, _mm256_and_si256(one, add_mask));
score = _mm256_sub_epi64(score, _mm256_and_si256(one, sub_mask));
horizontal_positive = _mm256_or_si256(_mm256_slli_epi64(horizontal_positive, 1), one);
horizontal_negative = _mm256_slli_epi64(horizontal_negative, 1);
__m256i const next_positive = or_nor_(horizontal_negative, carry_in, horizontal_positive, ones);
__m256i const next_negative = _mm256_and_si256(horizontal_positive, carry_in);
vertical_positive = _mm256_blendv_epi8(vertical_positive, next_positive, active);
vertical_negative = _mm256_blendv_epi8(vertical_negative, next_negative, active);
}
alignas(32) u64_t final_scores[lanes_k];
_mm256_store_si256((__m256i *)final_scores, score);
for (index_t lane_index = 0; lane_index != pairs.lanes_count(); ++lane_index)
results[pairs.positions[lane_index]] = (size_t)final_scores[lane_index];
return status_t::success_k;
}
template <size_t words_count_, typename results_writer_>
status_t distances_4x_multiword_(lane_pairs_view<char_t> const &pairs, results_writer_ &results,
scratch_space_t scratch_space) const noexcept {
constexpr size_t words_count = words_count_;
size_t max_longer = 0;
for (index_t lane_index = 0; lane_index != pairs.lanes_count(); ++lane_index)
max_longer = sz_max_of_two(max_longer, pairs.longers[lane_index].size());
size_t const match_masks_words = (size_t)256 * words_count * lanes_k;
if (scratch_space.size() < match_masks_words * sizeof(u64_t)) return status_t::bad_alloc_k;
u64_t *const match_masks = reinterpret_cast<u64_t *>(scratch_space.data());
for (size_t element = 0; element != match_masks_words; ++element) match_masks[element] = 0;
alignas(32) u64_t top_bits[lanes_k] = {0}, shorter_lengths[lanes_k] = {0}, longer_lengths[lanes_k] = {0};
for (index_t lane_index = 0; lane_index != pairs.lanes_count(); ++lane_index) {
index_t const shorter_length = (index_t)pairs.shorters[lane_index].size();
char_t const *const shorter = pairs.shorters[lane_index].data();
u64_t *const lane_table = match_masks + (size_t)lane_index * 256 * words_count;
for (index_t position = 0; position != shorter_length; ++position)
lane_table[(size_t)(u8_t)shorter[position] * words_count + (position >> 6)] |= (u64_t)1
<< (position & 63);
top_bits[lane_index] = (u64_t)1 << ((shorter_length - 1) & 63);
shorter_lengths[lane_index] = shorter_length;
longer_lengths[lane_index] = pairs.longers[lane_index].size();
}
__m256i vertical_positive[words_count_];
__m256i vertical_negative[words_count_];
for (size_t word = 0; word != words_count; ++word) {
vertical_positive[word] = _mm256_set1_epi64x(-1);
vertical_negative[word] = _mm256_setzero_si256();
}
__m256i const one = _mm256_set1_epi64x(1);
__m256i const ones = _mm256_set1_epi64x(-1);
__m256i const zero = _mm256_setzero_si256();
__m256i const sign_bit = _mm256_set1_epi64x((long long)((u64_t)1 << 63));
__m256i const top_mask = _mm256_load_si256((__m256i const *)top_bits);
__m256i const longer_vec = _mm256_load_si256((__m256i const *)longer_lengths);
__m256i score = _mm256_load_si256((__m256i const *)shorter_lengths);
constexpr size_t last_word = words_count_ - 1;
for (size_t position = 0; position != max_longer; ++position) {
__m256i const active = _mm256_cmpgt_epi64(longer_vec, _mm256_set1_epi64x((long long)position));
alignas(32) u64_t base_offsets[lanes_k] = {0};
for (index_t lane_index = 0; lane_index != lanes_k; ++lane_index) {
bool const lane_active = lane_index < pairs.lanes_count() &&
position < pairs.longers[lane_index].size();
u8_t const symbol = lane_active ? (u8_t)pairs.longers[lane_index].data()[position] : 0;
base_offsets[lane_index] = lane_active
? (u64_t)lane_index * 256 * words_count + (u64_t)symbol * words_count
: 0;
}
__m256i addition_carry = _mm256_setzero_si256(); __m256i horizontal_positive_carry = one; __m256i horizontal_negative_carry = _mm256_setzero_si256();
for (size_t word = 0; word != words_count; ++word) {
alignas(32) u64_t equality_words[lanes_k];
for (index_t lane_index = 0; lane_index != lanes_k; ++lane_index)
equality_words[lane_index] = (lane_index < pairs.lanes_count() &&
position < pairs.longers[lane_index].size())
? match_masks[(size_t)base_offsets[lane_index] + word]
: 0;
__m256i const equality = _mm256_load_si256((__m256i const *)equality_words);
__m256i const vertical_positive_word = vertical_positive[word];
__m256i const vertical_negative_word = vertical_negative[word];
__m256i const summand = _mm256_and_si256(equality, vertical_positive_word);
__m256i const sum_low = _mm256_add_epi64(summand, vertical_positive_word);
__m256i const carry_from_summand = lane_less_unsigned_(sum_low, summand, sign_bit);
__m256i const sum = _mm256_add_epi64(sum_low, addition_carry);
__m256i const carry_from_incoming = lane_less_unsigned_(sum, sum_low, sign_bit);
addition_carry = _mm256_and_si256(one, _mm256_or_si256(carry_from_summand, carry_from_incoming));
__m256i const carry_in = _mm256_or_si256(equality, vertical_negative_word); __m256i const diagonal = _mm256_or_si256(_mm256_xor_si256(sum, vertical_positive_word), carry_in);
__m256i horizontal_positive = or_nor_(vertical_negative_word, diagonal, vertical_positive_word,
ones); __m256i horizontal_negative = _mm256_and_si256(vertical_positive_word, diagonal);
if (word == last_word) {
__m256i const add_mask = _mm256_and_si256(active, lane_test_(horizontal_positive, top_mask, zero));
__m256i const sub_mask = _mm256_and_si256(active, lane_test_(horizontal_negative, top_mask, zero));
score = _mm256_add_epi64(score, _mm256_and_si256(one, add_mask));
score = _mm256_sub_epi64(score, _mm256_and_si256(one, sub_mask));
}
__m256i const next_positive_carry = _mm256_srli_epi64(horizontal_positive, 63);
__m256i const next_negative_carry = _mm256_srli_epi64(horizontal_negative, 63);
horizontal_positive = _mm256_or_si256(_mm256_slli_epi64(horizontal_positive, 1),
horizontal_positive_carry);
horizontal_negative = _mm256_or_si256(_mm256_slli_epi64(horizontal_negative, 1),
horizontal_negative_carry);
horizontal_positive_carry = next_positive_carry;
horizontal_negative_carry = next_negative_carry;
__m256i const next_positive = or_nor_(horizontal_negative, carry_in, horizontal_positive, ones);
__m256i const next_negative = _mm256_and_si256(horizontal_positive, carry_in);
vertical_positive[word] = _mm256_blendv_epi8(vertical_positive_word, next_positive, active);
vertical_negative[word] = _mm256_blendv_epi8(vertical_negative_word, next_negative, active);
}
}
alignas(32) u64_t final_scores[lanes_k];
_mm256_store_si256((__m256i *)final_scores, score);
for (index_t lane_index = 0; lane_index != pairs.lanes_count(); ++lane_index)
results[pairs.positions[lane_index]] = (size_t)final_scores[lane_index];
return status_t::success_k;
}
template <typename results_writer_>
status_t distances_4x_multiword_large_(lane_pairs_view<char_t> const &pairs, results_writer_ &results,
scratch_space_t scratch_space) const noexcept {
static constexpr size_t stack_words_capacity_k = 64;
size_t max_longer = 0, max_shorter = 0;
for (index_t lane_index = 0; lane_index != pairs.lanes_count(); ++lane_index) {
max_longer = sz_max_of_two(max_longer, pairs.longers[lane_index].size());
max_shorter = sz_max_of_two(max_shorter, pairs.shorters[lane_index].size());
}
size_t const words_count = divide_round_up(max_shorter, (size_t)64);
if (words_count == 0 || words_count > stack_words_capacity_k) return status_t::bad_alloc_k;
size_t const match_masks_words = (size_t)256 * words_count * lanes_k;
if (scratch_space.size() < match_masks_words * sizeof(u64_t)) return status_t::bad_alloc_k;
u64_t *const match_masks = reinterpret_cast<u64_t *>(scratch_space.data());
for (size_t element = 0; element != match_masks_words; ++element) match_masks[element] = 0;
alignas(32) u64_t top_bits[lanes_k] = {0}, shorter_lengths[lanes_k] = {0}, longer_lengths[lanes_k] = {0};
for (index_t lane_index = 0; lane_index != pairs.lanes_count(); ++lane_index) {
index_t const shorter_length = (index_t)pairs.shorters[lane_index].size();
char_t const *const shorter = pairs.shorters[lane_index].data();
u64_t *const lane_table = match_masks + (size_t)lane_index * 256 * words_count;
for (index_t position = 0; position != shorter_length; ++position)
lane_table[(size_t)(u8_t)shorter[position] * words_count + (position >> 6)] |= (u64_t)1
<< (position & 63);
top_bits[lane_index] = (u64_t)1 << ((shorter_length - 1) & 63);
shorter_lengths[lane_index] = shorter_length;
longer_lengths[lane_index] = pairs.longers[lane_index].size();
}
__m256i vertical_positive[stack_words_capacity_k];
__m256i vertical_negative[stack_words_capacity_k];
for (size_t word = 0; word != words_count; ++word) {
vertical_positive[word] = _mm256_set1_epi64x(-1);
vertical_negative[word] = _mm256_setzero_si256();
}
__m256i const one = _mm256_set1_epi64x(1);
__m256i const ones = _mm256_set1_epi64x(-1);
__m256i const zero = _mm256_setzero_si256();
__m256i const sign_bit = _mm256_set1_epi64x((long long)((u64_t)1 << 63));
__m256i const top_mask = _mm256_load_si256((__m256i const *)top_bits);
__m256i const longer_vec = _mm256_load_si256((__m256i const *)longer_lengths);
__m256i score = _mm256_load_si256((__m256i const *)shorter_lengths);
size_t const last_word = words_count - 1;
for (size_t position = 0; position != max_longer; ++position) {
__m256i const active = _mm256_cmpgt_epi64(longer_vec, _mm256_set1_epi64x((long long)position));
alignas(32) u64_t base_offsets[lanes_k] = {0};
for (index_t lane_index = 0; lane_index != lanes_k; ++lane_index) {
bool const lane_active = lane_index < pairs.lanes_count() &&
position < pairs.longers[lane_index].size();
u8_t const symbol = lane_active ? (u8_t)pairs.longers[lane_index].data()[position] : 0;
base_offsets[lane_index] = lane_active
? (u64_t)lane_index * 256 * words_count + (u64_t)symbol * words_count
: 0;
}
__m256i addition_carry = _mm256_setzero_si256();
__m256i horizontal_positive_carry = one;
__m256i horizontal_negative_carry = _mm256_setzero_si256();
for (size_t word = 0; word != words_count; ++word) {
alignas(32) u64_t equality_words[lanes_k];
for (index_t lane_index = 0; lane_index != lanes_k; ++lane_index)
equality_words[lane_index] = (lane_index < pairs.lanes_count() &&
position < pairs.longers[lane_index].size())
? match_masks[(size_t)base_offsets[lane_index] + word]
: 0;
__m256i const equality = _mm256_load_si256((__m256i const *)equality_words);
__m256i const vertical_positive_word = vertical_positive[word];
__m256i const vertical_negative_word = vertical_negative[word];
__m256i const summand = _mm256_and_si256(equality, vertical_positive_word);
__m256i const sum_low = _mm256_add_epi64(summand, vertical_positive_word);
__m256i const carry_from_summand = lane_less_unsigned_(sum_low, summand, sign_bit);
__m256i const sum = _mm256_add_epi64(sum_low, addition_carry);
__m256i const carry_from_incoming = lane_less_unsigned_(sum, sum_low, sign_bit);
addition_carry = _mm256_and_si256(one, _mm256_or_si256(carry_from_summand, carry_from_incoming));
__m256i const carry_in = _mm256_or_si256(equality, vertical_negative_word);
__m256i const diagonal = _mm256_or_si256(_mm256_xor_si256(sum, vertical_positive_word), carry_in);
__m256i horizontal_positive = or_nor_(vertical_negative_word, diagonal, vertical_positive_word, ones);
__m256i horizontal_negative = _mm256_and_si256(vertical_positive_word, diagonal);
if (word == last_word) {
__m256i const add_mask = _mm256_and_si256(active, lane_test_(horizontal_positive, top_mask, zero));
__m256i const sub_mask = _mm256_and_si256(active, lane_test_(horizontal_negative, top_mask, zero));
score = _mm256_add_epi64(score, _mm256_and_si256(one, add_mask));
score = _mm256_sub_epi64(score, _mm256_and_si256(one, sub_mask));
}
__m256i const next_positive_carry = _mm256_srli_epi64(horizontal_positive, 63);
__m256i const next_negative_carry = _mm256_srli_epi64(horizontal_negative, 63);
horizontal_positive = _mm256_or_si256(_mm256_slli_epi64(horizontal_positive, 1),
horizontal_positive_carry);
horizontal_negative = _mm256_or_si256(_mm256_slli_epi64(horizontal_negative, 1),
horizontal_negative_carry);
horizontal_positive_carry = next_positive_carry;
horizontal_negative_carry = next_negative_carry;
__m256i const next_positive = or_nor_(horizontal_negative, carry_in, horizontal_positive, ones);
__m256i const next_negative = _mm256_and_si256(horizontal_positive, carry_in);
vertical_positive[word] = _mm256_blendv_epi8(vertical_positive_word, next_positive, active);
vertical_negative[word] = _mm256_blendv_epi8(vertical_negative_word, next_negative, active);
}
}
alignas(32) u64_t final_scores[lanes_k];
_mm256_store_si256((__m256i *)final_scores, score);
for (index_t lane_index = 0; lane_index != pairs.lanes_count(); ++lane_index)
results[pairs.positions[lane_index]] = (size_t)final_scores[lane_index];
return status_t::success_k;
}
};
#pragma endregion Inter Sequence Byte Myers
#pragma region Inter Sequence Rune Myers
template <sz_capability_t capability_>
struct levenshtein_distance_myers<
rune_t, capability_,
std::enable_if_t<(capability_ & sz_cap_haswell_k) != 0 && (capability_ & sz_cap_icelake_k) == 0>> {
using char_t = rune_t;
using index_t = u32_t;
static constexpr index_t lanes_k = 4;
static constexpr size_t match_masks_bytes_k = sizeof(u64_t) * lanes_k * 256;
static constexpr rune_t empty_slot_k = static_cast<rune_t>(0xFFFFFFFFu);
levenshtein_distance_myers() noexcept {}
static __m256i lane_test_(__m256i value, __m256i probe, __m256i zero) noexcept {
__m256i const masked = _mm256_and_si256(value, probe);
return _mm256_andnot_si256(_mm256_cmpeq_epi64(masked, zero), _mm256_set1_epi64x(-1));
}
static __m256i lane_less_unsigned_(__m256i first, __m256i second, __m256i sign_bit) noexcept {
return _mm256_cmpgt_epi64(_mm256_xor_si256(second, sign_bit), _mm256_xor_si256(first, sign_bit));
}
static __m256i or_nor_(__m256i first, __m256i second, __m256i third, __m256i ones) noexcept {
return _mm256_or_si256(first, _mm256_andnot_si256(_mm256_or_si256(second, third), ones));
}
static size_t words_count_for(size_t shorter_length) noexcept {
return divide_round_up<size_t>(shorter_length, 64);
}
static index_t hash_capacity_for(size_t distinct_upper_bound) noexcept {
size_t const slots_wanted = sz_max_of_two(2 * distinct_upper_bound, (size_t)1);
return static_cast<index_t>(sz_size_bit_ceil(slots_wanted));
}
static index_t hash_rune(rune_t rune, index_t capacity) noexcept {
u64_t const mixed = static_cast<u64_t>(static_cast<u32_t>(rune)) * 0x9E3779B97F4A7C15ull;
return static_cast<index_t>((mixed >> 32) & static_cast<u64_t>(capacity - 1));
}
static size_t scratch_bytes_for(size_t max_shorter) noexcept {
size_t const words_count = words_count_for(sz_max_of_two(max_shorter, (size_t)1));
size_t const capacity = hash_capacity_for(sz_max_of_two(max_shorter, (size_t)1));
size_t const slot_keys_bytes = sizeof(rune_t) * capacity * lanes_k;
size_t const slot_masks_bytes = sizeof(u64_t) * capacity * words_count * lanes_k;
size_t const absent_row_bytes = sizeof(u64_t) * words_count;
return slot_keys_bytes + slot_masks_bytes + absent_row_bytes;
}
#pragma region Per Lane Hash match_masks
static bool build_lane_hashes_(span<char_t const> const *shorters, index_t pairs_active, index_t capacity,
size_t words_count, scratch_space_t scratch_space, rune_t *&slot_keys,
u64_t *&slot_masks, u64_t *&absent_row) noexcept {
size_t const slot_keys_bytes = sizeof(rune_t) * capacity * lanes_k;
size_t const slot_masks_bytes = sizeof(u64_t) * capacity * words_count * lanes_k;
size_t const absent_row_bytes = sizeof(u64_t) * words_count;
if (scratch_space.size() < slot_keys_bytes + slot_masks_bytes + absent_row_bytes) return false;
slot_keys = reinterpret_cast<rune_t *>(scratch_space.data());
slot_masks = reinterpret_cast<u64_t *>(scratch_space.data() + slot_keys_bytes);
absent_row = reinterpret_cast<u64_t *>(scratch_space.data() + slot_keys_bytes + slot_masks_bytes);
for (size_t word = 0; word != words_count; ++word) absent_row[word] = 0;
for (size_t slot = 0; slot != (size_t)capacity * lanes_k; ++slot) slot_keys[slot] = empty_slot_k;
for (index_t lane = 0; lane != pairs_active; ++lane) {
rune_t *const lane_keys = slot_keys + (size_t)lane * capacity;
u64_t *const lane_masks = slot_masks + (size_t)lane * capacity * words_count;
index_t const shorter_length = (index_t)shorters[lane].size();
char_t const *const shorter = shorters[lane].data();
for (index_t position = 0; position != shorter_length; ++position) {
rune_t const rune = shorter[position];
index_t slot = hash_rune(rune, capacity);
for (;; slot = (slot + 1) & (capacity - 1)) {
if (lane_keys[slot] == rune) break;
if (lane_keys[slot] == empty_slot_k) {
lane_keys[slot] = rune;
for (size_t word = 0; word != words_count; ++word)
lane_masks[(size_t)slot * words_count + word] = 0;
break;
}
}
lane_masks[(size_t)slot * words_count + (position >> 6)] |= (u64_t)1 << (position & 63);
}
}
return true;
}
static u64_t const *lane_match_row_(rune_t const *slot_keys, u64_t const *slot_masks, u64_t const *absent_row,
index_t lane, index_t capacity, size_t words_count, rune_t symbol) noexcept {
rune_t const *const lane_keys = slot_keys + (size_t)lane * capacity;
u64_t const *const lane_masks = slot_masks + (size_t)lane * capacity * words_count;
for (index_t slot = hash_rune(symbol, capacity);; slot = (slot + 1) & (capacity - 1)) {
rune_t const key = lane_keys[slot];
if (key == symbol) return &lane_masks[(size_t)slot * words_count];
if (key == empty_slot_k) break;
}
return absent_row;
}
#pragma endregion Per Lane Hash match_masks
template <typename results_writer_>
status_t distances_4x64_(lane_pairs_view<char_t> const &pairs, results_writer_ &results,
scratch_space_t scratch_space) const noexcept {
size_t max_longer = 0, max_shorter = 0;
for (index_t lane_index = 0; lane_index != pairs.lanes_count(); ++lane_index) {
max_longer = sz_max_of_two(max_longer, pairs.longers[lane_index].size());
max_shorter = sz_max_of_two(max_shorter, pairs.shorters[lane_index].size());
}
index_t const capacity = hash_capacity_for(sz_max_of_two(max_shorter, (size_t)1));
rune_t *slot_keys = nullptr;
u64_t *slot_masks = nullptr, *absent_row = nullptr;
if (!build_lane_hashes_(pairs.shorters.data(), (index_t)pairs.lanes_count(), capacity, 1, scratch_space,
slot_keys, slot_masks, absent_row))
return status_t::bad_alloc_k;
alignas(32) u64_t top_bits[lanes_k] = {0}, shorter_lengths[lanes_k] = {0}, longer_lengths[lanes_k] = {0};
for (index_t lane_index = 0; lane_index != pairs.lanes_count(); ++lane_index) {
index_t const shorter_length = (index_t)pairs.shorters[lane_index].size();
top_bits[lane_index] = (u64_t)1 << (shorter_length - 1);
shorter_lengths[lane_index] = shorter_length;
longer_lengths[lane_index] = pairs.longers[lane_index].size();
}
__m256i const one = _mm256_set1_epi64x(1);
__m256i const ones = _mm256_set1_epi64x(-1);
__m256i const zero = _mm256_setzero_si256();
__m256i const top_mask = _mm256_load_si256((__m256i const *)top_bits);
__m256i const longer_vec = _mm256_load_si256((__m256i const *)longer_lengths);
__m256i const length_vec = _mm256_load_si256((__m256i const *)shorter_lengths);
__m256i vertical_positive = _mm256_sub_epi64(_mm256_sllv_epi64(one, length_vec), one);
__m256i vertical_negative = _mm256_setzero_si256();
__m256i score = length_vec;
for (size_t position = 0; position != max_longer; ++position) {
__m256i const active = _mm256_cmpgt_epi64(longer_vec, _mm256_set1_epi64x((long long)position));
alignas(32) u64_t equality_words[lanes_k];
for (index_t lane = 0; lane != lanes_k; ++lane) {
bool const lane_active = lane < pairs.lanes_count() && position < pairs.longers[lane].size();
if (!lane_active) {
equality_words[lane] = 0;
continue;
}
rune_t const symbol = pairs.longers[lane].data()[position];
equality_words[lane] = lane_match_row_(slot_keys, slot_masks, absent_row, lane, capacity, 1, symbol)[0];
}
__m256i const equality = _mm256_load_si256((__m256i const *)equality_words);
__m256i const carry_in = _mm256_or_si256(equality, vertical_negative);
__m256i const sum = _mm256_add_epi64(_mm256_and_si256(equality, vertical_positive), vertical_positive);
__m256i const diagonal = _mm256_or_si256(_mm256_xor_si256(sum, vertical_positive), equality);
__m256i horizontal_positive = or_nor_(vertical_negative, diagonal, vertical_positive, ones); __m256i horizontal_negative = _mm256_and_si256(vertical_positive, diagonal); __m256i const add_mask = _mm256_and_si256(active, lane_test_(horizontal_positive, top_mask, zero));
__m256i const sub_mask = _mm256_and_si256(active, lane_test_(horizontal_negative, top_mask, zero));
score = _mm256_add_epi64(score, _mm256_and_si256(one, add_mask));
score = _mm256_sub_epi64(score, _mm256_and_si256(one, sub_mask));
horizontal_positive = _mm256_or_si256(_mm256_slli_epi64(horizontal_positive, 1), one);
horizontal_negative = _mm256_slli_epi64(horizontal_negative, 1);
__m256i const next_positive = or_nor_(horizontal_negative, carry_in, horizontal_positive, ones);
__m256i const next_negative = _mm256_and_si256(horizontal_positive, carry_in);
vertical_positive = _mm256_blendv_epi8(vertical_positive, next_positive, active);
vertical_negative = _mm256_blendv_epi8(vertical_negative, next_negative, active);
}
alignas(32) u64_t final_scores[lanes_k];
_mm256_store_si256((__m256i *)final_scores, score);
for (index_t lane_index = 0; lane_index != pairs.lanes_count(); ++lane_index)
results[pairs.positions[lane_index]] = (size_t)final_scores[lane_index];
return status_t::success_k;
}
template <size_t words_count_, typename results_writer_>
status_t distances_4x_multiword_(lane_pairs_view<char_t> const &pairs, results_writer_ &results,
scratch_space_t scratch_space) const noexcept {
constexpr size_t words_count = words_count_;
size_t max_longer = 0, max_shorter = 0;
for (index_t lane_index = 0; lane_index != pairs.lanes_count(); ++lane_index) {
max_longer = sz_max_of_two(max_longer, pairs.longers[lane_index].size());
max_shorter = sz_max_of_two(max_shorter, pairs.shorters[lane_index].size());
}
index_t const capacity = hash_capacity_for(sz_max_of_two(max_shorter, (size_t)1));
rune_t *slot_keys = nullptr;
u64_t *slot_masks = nullptr, *absent_row = nullptr;
if (!build_lane_hashes_(pairs.shorters.data(), (index_t)pairs.lanes_count(), capacity, words_count,
scratch_space, slot_keys, slot_masks, absent_row))
return status_t::bad_alloc_k;
alignas(32) u64_t top_bits[lanes_k] = {0}, shorter_lengths[lanes_k] = {0}, longer_lengths[lanes_k] = {0};
for (index_t lane_index = 0; lane_index != pairs.lanes_count(); ++lane_index) {
index_t const shorter_length = (index_t)pairs.shorters[lane_index].size();
top_bits[lane_index] = (u64_t)1 << ((shorter_length - 1) & 63);
shorter_lengths[lane_index] = shorter_length;
longer_lengths[lane_index] = pairs.longers[lane_index].size();
}
__m256i vertical_positive[words_count_];
__m256i vertical_negative[words_count_];
for (size_t word = 0; word != words_count; ++word) {
vertical_positive[word] = _mm256_set1_epi64x(-1);
vertical_negative[word] = _mm256_setzero_si256();
}
__m256i const one = _mm256_set1_epi64x(1);
__m256i const ones = _mm256_set1_epi64x(-1);
__m256i const zero = _mm256_setzero_si256();
__m256i const sign_bit = _mm256_set1_epi64x((long long)((u64_t)1 << 63));
__m256i const top_mask = _mm256_load_si256((__m256i const *)top_bits);
__m256i const longer_vec = _mm256_load_si256((__m256i const *)longer_lengths);
__m256i score = _mm256_load_si256((__m256i const *)shorter_lengths);
constexpr size_t last_word = words_count_ - 1;
for (size_t position = 0; position != max_longer; ++position) {
__m256i const active = _mm256_cmpgt_epi64(longer_vec, _mm256_set1_epi64x((long long)position));
u64_t const *match_rows[lanes_k];
for (index_t lane = 0; lane != lanes_k; ++lane) {
bool const lane_active = lane < pairs.lanes_count() && position < pairs.longers[lane].size();
rune_t const symbol = lane_active ? pairs.longers[lane].data()[position] : empty_slot_k;
match_rows[lane] = lane_active ? lane_match_row_(slot_keys, slot_masks, absent_row, lane, capacity,
words_count, symbol)
: absent_row;
}
__m256i addition_carry = _mm256_setzero_si256(); __m256i horizontal_positive_carry = one; __m256i horizontal_negative_carry = _mm256_setzero_si256();
for (size_t word = 0; word != words_count; ++word) {
alignas(32) u64_t equality_words[lanes_k];
for (index_t lane = 0; lane != lanes_k; ++lane)
equality_words[lane] = (lane < pairs.lanes_count() && position < pairs.longers[lane].size())
? match_rows[lane][word]
: 0;
__m256i const equality = _mm256_load_si256((__m256i const *)equality_words);
__m256i const vertical_positive_word = vertical_positive[word];
__m256i const vertical_negative_word = vertical_negative[word];
__m256i const summand = _mm256_and_si256(equality, vertical_positive_word);
__m256i const sum_low = _mm256_add_epi64(summand, vertical_positive_word);
__m256i const carry_from_summand = lane_less_unsigned_(sum_low, summand, sign_bit);
__m256i const sum = _mm256_add_epi64(sum_low, addition_carry);
__m256i const carry_from_incoming = lane_less_unsigned_(sum, sum_low, sign_bit);
addition_carry = _mm256_and_si256(one, _mm256_or_si256(carry_from_summand, carry_from_incoming));
__m256i const carry_in = _mm256_or_si256(equality, vertical_negative_word); __m256i const diagonal = _mm256_or_si256(_mm256_xor_si256(sum, vertical_positive_word), carry_in);
__m256i horizontal_positive = or_nor_(vertical_negative_word, diagonal, vertical_positive_word,
ones); __m256i horizontal_negative = _mm256_and_si256(vertical_positive_word, diagonal);
if (word == last_word) {
__m256i const add_mask = _mm256_and_si256(active, lane_test_(horizontal_positive, top_mask, zero));
__m256i const sub_mask = _mm256_and_si256(active, lane_test_(horizontal_negative, top_mask, zero));
score = _mm256_add_epi64(score, _mm256_and_si256(one, add_mask));
score = _mm256_sub_epi64(score, _mm256_and_si256(one, sub_mask));
}
__m256i const next_positive_carry = _mm256_srli_epi64(horizontal_positive, 63);
__m256i const next_negative_carry = _mm256_srli_epi64(horizontal_negative, 63);
horizontal_positive = _mm256_or_si256(_mm256_slli_epi64(horizontal_positive, 1),
horizontal_positive_carry);
horizontal_negative = _mm256_or_si256(_mm256_slli_epi64(horizontal_negative, 1),
horizontal_negative_carry);
horizontal_positive_carry = next_positive_carry;
horizontal_negative_carry = next_negative_carry;
__m256i const next_positive = or_nor_(horizontal_negative, carry_in, horizontal_positive, ones);
__m256i const next_negative = _mm256_and_si256(horizontal_positive, carry_in);
vertical_positive[word] = _mm256_blendv_epi8(vertical_positive_word, next_positive, active);
vertical_negative[word] = _mm256_blendv_epi8(vertical_negative_word, next_negative, active);
}
}
alignas(32) u64_t final_scores[lanes_k];
_mm256_store_si256((__m256i *)final_scores, score);
for (index_t lane_index = 0; lane_index != pairs.lanes_count(); ++lane_index)
results[pairs.positions[lane_index]] = (size_t)final_scores[lane_index];
return status_t::success_k;
}
template <typename results_writer_>
status_t distances_4x_multiword_large_(lane_pairs_view<char_t> const &pairs, results_writer_ &results,
scratch_space_t scratch_space) const noexcept {
static constexpr size_t stack_words_capacity_k = 64;
size_t max_longer = 0, max_shorter = 0;
for (index_t lane_index = 0; lane_index != pairs.lanes_count(); ++lane_index) {
max_longer = sz_max_of_two(max_longer, pairs.longers[lane_index].size());
max_shorter = sz_max_of_two(max_shorter, pairs.shorters[lane_index].size());
}
size_t const words_count = words_count_for(sz_max_of_two(max_shorter, (size_t)1));
if (words_count == 0 || words_count > stack_words_capacity_k) return status_t::bad_alloc_k;
index_t const capacity = hash_capacity_for(sz_max_of_two(max_shorter, (size_t)1));
rune_t *slot_keys = nullptr;
u64_t *slot_masks = nullptr, *absent_row = nullptr;
if (!build_lane_hashes_(pairs.shorters.data(), (index_t)pairs.lanes_count(), capacity, words_count,
scratch_space, slot_keys, slot_masks, absent_row))
return status_t::bad_alloc_k;
alignas(32) u64_t top_bits[lanes_k] = {0}, shorter_lengths[lanes_k] = {0}, longer_lengths[lanes_k] = {0};
for (index_t lane_index = 0; lane_index != pairs.lanes_count(); ++lane_index) {
index_t const shorter_length = (index_t)pairs.shorters[lane_index].size();
top_bits[lane_index] = (u64_t)1 << ((shorter_length - 1) & 63);
shorter_lengths[lane_index] = shorter_length;
longer_lengths[lane_index] = pairs.longers[lane_index].size();
}
__m256i vertical_positive[stack_words_capacity_k];
__m256i vertical_negative[stack_words_capacity_k];
for (size_t word = 0; word != words_count; ++word) {
vertical_positive[word] = _mm256_set1_epi64x(-1);
vertical_negative[word] = _mm256_setzero_si256();
}
__m256i const one = _mm256_set1_epi64x(1);
__m256i const ones = _mm256_set1_epi64x(-1);
__m256i const zero = _mm256_setzero_si256();
__m256i const sign_bit = _mm256_set1_epi64x((long long)((u64_t)1 << 63));
__m256i const top_mask = _mm256_load_si256((__m256i const *)top_bits);
__m256i const longer_vec = _mm256_load_si256((__m256i const *)longer_lengths);
__m256i score = _mm256_load_si256((__m256i const *)shorter_lengths);
size_t const last_word = words_count - 1;
for (size_t position = 0; position != max_longer; ++position) {
__m256i const active = _mm256_cmpgt_epi64(longer_vec, _mm256_set1_epi64x((long long)position));
u64_t const *match_rows[lanes_k];
for (index_t lane = 0; lane != lanes_k; ++lane) {
bool const lane_active = lane < pairs.lanes_count() && position < pairs.longers[lane].size();
rune_t const symbol = lane_active ? pairs.longers[lane].data()[position] : empty_slot_k;
match_rows[lane] = lane_active ? lane_match_row_(slot_keys, slot_masks, absent_row, lane, capacity,
words_count, symbol)
: absent_row;
}
__m256i addition_carry = _mm256_setzero_si256();
__m256i horizontal_positive_carry = one;
__m256i horizontal_negative_carry = _mm256_setzero_si256();
for (size_t word = 0; word != words_count; ++word) {
alignas(32) u64_t equality_words[lanes_k];
for (index_t lane = 0; lane != lanes_k; ++lane)
equality_words[lane] = (lane < pairs.lanes_count() && position < pairs.longers[lane].size())
? match_rows[lane][word]
: 0;
__m256i const equality = _mm256_load_si256((__m256i const *)equality_words);
__m256i const vertical_positive_word = vertical_positive[word];
__m256i const vertical_negative_word = vertical_negative[word];
__m256i const summand = _mm256_and_si256(equality, vertical_positive_word);
__m256i const sum_low = _mm256_add_epi64(summand, vertical_positive_word);
__m256i const carry_from_summand = lane_less_unsigned_(sum_low, summand, sign_bit);
__m256i const sum = _mm256_add_epi64(sum_low, addition_carry);
__m256i const carry_from_incoming = lane_less_unsigned_(sum, sum_low, sign_bit);
addition_carry = _mm256_and_si256(one, _mm256_or_si256(carry_from_summand, carry_from_incoming));
__m256i const carry_in = _mm256_or_si256(equality, vertical_negative_word);
__m256i const diagonal = _mm256_or_si256(_mm256_xor_si256(sum, vertical_positive_word), carry_in);
__m256i horizontal_positive = or_nor_(vertical_negative_word, diagonal, vertical_positive_word, ones);
__m256i horizontal_negative = _mm256_and_si256(vertical_positive_word, diagonal);
if (word == last_word) {
__m256i const add_mask = _mm256_and_si256(active, lane_test_(horizontal_positive, top_mask, zero));
__m256i const sub_mask = _mm256_and_si256(active, lane_test_(horizontal_negative, top_mask, zero));
score = _mm256_add_epi64(score, _mm256_and_si256(one, add_mask));
score = _mm256_sub_epi64(score, _mm256_and_si256(one, sub_mask));
}
__m256i const next_positive_carry = _mm256_srli_epi64(horizontal_positive, 63);
__m256i const next_negative_carry = _mm256_srli_epi64(horizontal_negative, 63);
horizontal_positive = _mm256_or_si256(_mm256_slli_epi64(horizontal_positive, 1),
horizontal_positive_carry);
horizontal_negative = _mm256_or_si256(_mm256_slli_epi64(horizontal_negative, 1),
horizontal_negative_carry);
horizontal_positive_carry = next_positive_carry;
horizontal_negative_carry = next_negative_carry;
__m256i const next_positive = or_nor_(horizontal_negative, carry_in, horizontal_positive, ones);
__m256i const next_negative = _mm256_and_si256(horizontal_positive, carry_in);
vertical_positive[word] = _mm256_blendv_epi8(vertical_positive_word, next_positive, active);
vertical_negative[word] = _mm256_blendv_epi8(vertical_negative_word, next_negative, active);
}
}
alignas(32) u64_t final_scores[lanes_k];
_mm256_store_si256((__m256i *)final_scores, score);
for (index_t lane_index = 0; lane_index != pairs.lanes_count(); ++lane_index)
results[pairs.positions[lane_index]] = (size_t)final_scores[lane_index];
return status_t::success_k;
}
};
#pragma endregion Inter Sequence Rune Myers
#pragma region Inter Sequence Cross Product Engines
template <typename allocator_type_, sz_capability_t capability_>
struct levenshtein_distances<
linear_gap_costs_t, allocator_type_, capability_,
std::enable_if_t<(capability_ & sz_cap_haswell_k) != 0 && (capability_ & sz_cap_icelake_k) == 0>> {
using char_t = char;
using gap_costs_t = linear_gap_costs_t;
using allocator_t = allocator_type_;
using index_t = u32_t;
static constexpr sz_capability_t capability_k = capability_;
static constexpr size_t candidate_lanes_k = 16; using scoring_t = levenshtein_distance<char, gap_costs_t, sz_cap_serial_k>; using myers_t = levenshtein_distance_myers<char, capability_k>; using lane_walker_narrow_t =
candidate_lane_walker<char, u16_t, uniform_substitution_costs_t, gap_costs_t, sz_minimize_distance_k,
sz_similarity_global_k, sz_cap_haswell_k, (int)candidate_lanes_k,
void>; using lane_walker_wide_t =
candidate_lane_walker<char, u32_t, uniform_substitution_costs_t, gap_costs_t, sz_minimize_distance_k,
sz_similarity_global_k, sz_cap_haswell_k, 8,
void>; using scratch_allocator_t = typename std::allocator_traits<allocator_t>::template rebind_alloc<std::byte>;
uniform_substitution_costs_t substituter_ {};
linear_gap_costs_t gap_costs_ {};
allocator_t alloc_ {};
safe_vector<std::byte, scratch_allocator_t> score_scratch_ {alloc_};
levenshtein_distances(allocator_t alloc = {}) noexcept : alloc_(alloc) {}
levenshtein_distances(uniform_substitution_costs_t subs, linear_gap_costs_t gaps,
allocator_t alloc = allocator_t {}) noexcept
: substituter_(subs), gap_costs_(gaps), alloc_(alloc) {}
template <typename queries_type_, typename candidates_type_>
size_t worst_cell_scratch_(queries_type_ const &queries, candidates_type_ const &candidates,
cpu_specs_t const &specs) const noexcept {
size_t longest_query = 0, longest_query_index = 0, longest_candidate = 0, longest_candidate_index = 0;
for (size_t index = 0; index < queries.size(); ++index)
if (to_view(queries[index]).size() > longest_query)
longest_query = to_view(queries[index]).size(), longest_query_index = index;
for (size_t index = 0; index < candidates.size(); ++index)
if (to_view(candidates[index]).size() > longest_candidate)
longest_candidate = to_view(candidates[index]).size(), longest_candidate_index = index;
size_t const max_longer = sz_max_of_two(longest_query, longest_candidate);
size_t const myers_scratch = myers_t::match_masks_bytes_k + max_longer * (size_t)myers_t::lanes_k;
size_t dp_scratch = 0, fourxN_scratch = 0;
size_t const shortest_longest = sz_min_of_two(longest_query, longest_candidate);
if (queries.size() && candidates.size() && shortest_longest > 64) {
size_t const words_bound = divide_round_up(shortest_longest, (size_t)64);
fourxN_scratch = myers_t::match_masks_bytes_k * words_bound;
}
if (queries.size() && candidates.size() && shortest_longest > 512) {
scoring_t dp {substituter_, gap_costs_};
dp_scratch = dp.scratch_space_needed(to_view(queries[longest_query_index]),
to_view(candidates[longest_candidate_index]), specs);
}
return sz_max_of_two(sz_max_of_two(myers_scratch, dp_scratch), fourxN_scratch);
}
#pragma region Cross Product Scoring
template <typename queries_type_, typename candidates_type_, typename results_type_>
SZ_NOINLINE status_t score_range_(queries_type_ const &queries, candidates_type_ const &candidates,
results_type_ &&results, cross_similarities_t cross_kind, size_t cell_begin,
size_t cell_end, scratch_space_t scratch, cpu_specs_t const &specs) noexcept {
using value_t = remove_cvref<decltype(results.data[0])>;
size_t const candidates_count = candidates.size();
scoring_t dp {substituter_, gap_costs_};
auto const destination_for = [&](size_t query_index, size_t candidate_index) noexcept {
cross_cell_destination_t<value_t> destination;
destination.primary = results.data + query_index * results.row_stride + candidate_index;
if (cross_kind == cross_similarities_t::symmetric_k && candidate_index != query_index)
destination.mirror = results.data + candidate_index * results.row_stride + query_index;
return destination;
};
myers_t myers;
cross_cell_writer_t<value_t> writer;
dummy_executor_t dummy;
for (size_t cell_index = cell_begin; cell_index != cell_end;) {
size_t query_index = 0, candidate_index = 0;
cross_cell_to_indices_(cell_index, candidates_count, cross_kind, query_index, candidate_index);
auto const query = to_view(queries[query_index]);
auto const candidate = to_view(candidates[candidate_index]);
size_t const shorter = sz_min_of_two(query.size(), candidate.size());
if (shorter == 0) {
cross_cell_destination_t<value_t> const destination = destination_for(query_index, candidate_index);
cross_cell_writer_t<value_t> {&destination}[0] = sz_max_of_two(query.size(), candidate.size());
++cell_index;
continue;
}
if (shorter <= 64) {
span<char const> group_shorters[myers_t::lanes_k], group_longers[myers_t::lanes_k];
size_t group_positions[myers_t::lanes_k];
cross_cell_destination_t<value_t> group_destinations[myers_t::lanes_k];
bool const seed_query_shorter = query.size() <= candidate.size();
group_shorters[0] = seed_query_shorter ? query : candidate;
group_longers[0] = seed_query_shorter ? candidate : query;
group_positions[0] = 0;
group_destinations[0] = destination_for(query_index, candidate_index);
index_t group = 1;
++cell_index;
for (; cell_index != cell_end && group != (index_t)myers_t::lanes_k; ++cell_index, ++group) {
size_t next_query_index = 0, next_candidate_index = 0;
cross_cell_to_indices_(cell_index, candidates_count, cross_kind, next_query_index,
next_candidate_index);
auto const next_query = to_view(queries[next_query_index]);
auto const next_candidate = to_view(candidates[next_candidate_index]);
size_t const next_shorter = sz_min_of_two(next_query.size(), next_candidate.size());
if (next_shorter == 0 || next_shorter > 64) break;
bool const next_query_shorter = next_query.size() <= next_candidate.size();
group_shorters[group] = next_query_shorter ? next_query : next_candidate;
group_longers[group] = next_query_shorter ? next_candidate : next_query;
group_positions[group] = group;
group_destinations[group] = destination_for(next_query_index, next_candidate_index);
}
writer.destinations = group_destinations;
status_t const status = myers.distances_4x64_(
lane_pairs_view<char> {{group_shorters, group}, {group_longers, group}, {group_positions, group}},
writer, scratch);
if (status != status_t::success_k) return status;
continue;
}
size_t const seed_bucket = divide_round_up(shorter, (size_t)64);
span<char const> group_shorters[myers_t::lanes_k], group_longers[myers_t::lanes_k];
size_t group_positions[myers_t::lanes_k];
cross_cell_destination_t<value_t> group_destinations[myers_t::lanes_k];
bool const seed_query_shorter = query.size() <= candidate.size();
group_shorters[0] = seed_query_shorter ? query : candidate;
group_longers[0] = seed_query_shorter ? candidate : query;
group_positions[0] = 0;
group_destinations[0] = destination_for(query_index, candidate_index);
index_t group = 1;
++cell_index;
for (; cell_index != cell_end && group != (index_t)myers_t::lanes_k; ++cell_index, ++group) {
size_t next_query_index = 0, next_candidate_index = 0;
cross_cell_to_indices_(cell_index, candidates_count, cross_kind, next_query_index,
next_candidate_index);
auto const next_query = to_view(queries[next_query_index]);
auto const next_candidate = to_view(candidates[next_candidate_index]);
size_t const next_shorter = sz_min_of_two(next_query.size(), next_candidate.size());
if (next_shorter <= 64 || divide_round_up(next_shorter, (size_t)64) != seed_bucket) break;
bool const next_query_shorter = next_query.size() <= next_candidate.size();
group_shorters[group] = next_query_shorter ? next_query : next_candidate;
group_longers[group] = next_query_shorter ? next_candidate : next_query;
group_positions[group] = group;
group_destinations[group] = destination_for(next_query_index, next_candidate_index);
}
cross_cell_writer_t<value_t> group_writer;
group_writer.destinations = group_destinations;
lane_pairs_view<char> const group_pairs {{group_shorters, group},
{group_longers, group},
{group_positions, group}};
if (seed_bucket > 8 && group < 2) {
size_t result_score = 0;
if (status_t const lone_status = dp(group_shorters[0], group_longers[0], result_score, scratch, dummy,
specs);
lone_status != status_t::success_k)
return lone_status;
cross_cell_writer_t<value_t> {&group_destinations[0]}[0] = result_score;
continue;
}
status_t status = dispatch_word_bucket_<2, 8>(
seed_bucket,
[&](auto bucket) {
return myers.template distances_4x_multiword_<bucket.value>(group_pairs, group_writer, scratch);
},
[&] { return myers.distances_4x_multiword_large_(group_pairs, group_writer, scratch); });
if (status == status_t::success_k) continue;
for (index_t lane = 0; lane != group; ++lane) {
size_t lane_score = 0;
if (status_t const lane_status = dp(group_shorters[lane], group_longers[lane], lane_score, scratch,
dummy, specs);
lane_status != status_t::success_k)
return lane_status;
cross_cell_writer_t<value_t> {&group_destinations[lane]}[0] = lane_score;
}
}
return status_t::success_k;
}
template <typename queries_type_, typename candidates_type_, typename results_type_, typename executor_type_>
SZ_NOINLINE status_t score_parallel_(queries_type_ const &queries, candidates_type_ const &candidates,
results_type_ &&results, cross_similarities_t cross_kind,
executor_type_ &&executor, cpu_specs_t const &specs) noexcept {
size_t const cells_count = cross_live_cells_count_(queries.size(), candidates.size(), cross_kind);
size_t const worker_scratch = worst_cell_scratch_(queries, candidates, specs);
size_t const workers = sz_max_of_two(executor.threads_count(), (size_t)1);
if (status_t status = score_scratch_.try_resize(worker_scratch * workers); status != status_t::success_k)
return status;
using prong_t = typename remove_cvref<executor_type_>::prong_t;
schedule_batches_t const schedule_batches = schedule_batches_(cells_count, workers,
lockstep_lanes_of_<myers_t>::value);
atomic_status_t status;
executor.for_n_dynamic(schedule_batches.batches_count, [&](prong_t prong) noexcept {
if (status != status_t::success_k) return;
scratch_space_t slice =
scratch_space_t(score_scratch_).subspan(prong.thread * worker_scratch, worker_scratch);
status = score_range_(queries, candidates, results, cross_kind, schedule_batches.batch_begin(prong.task),
schedule_batches.batch_end(prong.task, cells_count), slice, specs);
});
return status;
}
#pragma endregion Cross Product Scoring
#pragma region Public Cross Product Overloads
template <typename queries_type_, typename candidates_type_, typename value_type_>
SZ_NOIPA status_t operator()(queries_type_ const &queries, candidates_type_ const &candidates,
strided_rows<value_type_> results, cpu_specs_t const &specs = {}) noexcept {
if (!is_unit_cost(substituter_, gap_costs_)) {
lane_walker_narrow_t narrow {substituter_, gap_costs_};
lane_walker_wide_t wide {substituter_, gap_costs_};
scoring_t fallback {substituter_, gap_costs_};
if (status_t status = score_scratch_.try_resize(
cross_product_candidate_lanes_scratch_(narrow, wide, fallback, queries, candidates, specs));
status != status_t::success_k)
return status;
return cross_product_candidate_lanes_range_(
narrow, wide, fallback, queries, candidates, results, cross_similarities_t::all_pairs_k, 0,
cross_live_cells_count_(queries.size(), candidates.size(), cross_similarities_t::all_pairs_k),
scratch_space_t(score_scratch_), specs);
}
if (status_t status = score_scratch_.try_resize(worst_cell_scratch_(queries, candidates, specs));
status != status_t::success_k)
return status;
return score_range_(
queries, candidates, results, cross_similarities_t::all_pairs_k, 0,
cross_live_cells_count_(queries.size(), candidates.size(), cross_similarities_t::all_pairs_k),
scratch_space_t(score_scratch_), specs);
}
template <typename queries_type_, typename candidates_type_, typename value_type_, typename executor_type_>
SZ_NOIPA status_t operator()(queries_type_ const &queries, candidates_type_ const &candidates,
strided_rows<value_type_> results, executor_type_ &&executor,
cpu_specs_t const &specs = {}) noexcept {
if (!is_unit_cost(substituter_, gap_costs_)) {
lane_walker_narrow_t narrow {substituter_, gap_costs_};
lane_walker_wide_t wide {substituter_, gap_costs_};
scoring_t fallback {substituter_, gap_costs_};
return cross_product_candidate_lanes_parallel_(narrow, wide, fallback, queries, candidates, results,
cross_similarities_t::all_pairs_k, score_scratch_,
std::forward<executor_type_>(executor), specs);
}
return score_parallel_(queries, candidates, results, cross_similarities_t::all_pairs_k,
std::forward<executor_type_>(executor), specs);
}
template <typename sequences_type_, typename value_type_>
SZ_NOIPA status_t operator()(sequences_type_ const &sequences, strided_rows<value_type_> results,
cpu_specs_t const &specs = {}) noexcept {
if (!is_unit_cost(substituter_, gap_costs_)) {
lane_walker_narrow_t narrow {substituter_, gap_costs_};
lane_walker_wide_t wide {substituter_, gap_costs_};
scoring_t fallback {substituter_, gap_costs_};
if (status_t status = score_scratch_.try_resize(
cross_product_candidate_lanes_scratch_(narrow, wide, fallback, sequences, sequences, specs));
status != status_t::success_k)
return status;
return cross_product_candidate_lanes_range_(
narrow, wide, fallback, sequences, sequences, results, cross_similarities_t::symmetric_k, 0,
cross_live_cells_count_(sequences.size(), sequences.size(), cross_similarities_t::symmetric_k),
scratch_space_t(score_scratch_), specs);
}
if (status_t status = score_scratch_.try_resize(worst_cell_scratch_(sequences, sequences, specs));
status != status_t::success_k)
return status;
return score_range_(
sequences, sequences, results, cross_similarities_t::symmetric_k, 0,
cross_live_cells_count_(sequences.size(), sequences.size(), cross_similarities_t::symmetric_k),
scratch_space_t(score_scratch_), specs);
}
template <typename sequences_type_, typename value_type_, typename executor_type_>
SZ_NOIPA status_t operator()(sequences_type_ const &sequences, strided_rows<value_type_> results,
executor_type_ &&executor, cpu_specs_t const &specs = {}) noexcept {
if (!is_unit_cost(substituter_, gap_costs_)) {
lane_walker_narrow_t narrow {substituter_, gap_costs_};
lane_walker_wide_t wide {substituter_, gap_costs_};
scoring_t fallback {substituter_, gap_costs_};
return cross_product_candidate_lanes_parallel_(narrow, wide, fallback, sequences, sequences, results,
cross_similarities_t::symmetric_k, score_scratch_,
std::forward<executor_type_>(executor), specs);
}
return score_parallel_(sequences, sequences, results, cross_similarities_t::symmetric_k,
std::forward<executor_type_>(executor), specs);
}
#pragma endregion Public Cross Product Overloads
};
template <typename allocator_type_, sz_capability_t capability_>
struct levenshtein_distances<
affine_gap_costs_t, allocator_type_, capability_,
std::enable_if_t<(capability_ & sz_cap_haswell_k) != 0 && (capability_ & sz_cap_icelake_k) == 0>> {
using char_t = char;
using gap_costs_t = affine_gap_costs_t;
using allocator_t = allocator_type_;
using index_t = u32_t;
static constexpr sz_capability_t capability_k = capability_;
static constexpr size_t candidate_lanes_k = 16;
using scoring_t = levenshtein_distance<char, affine_gap_costs_t, sz_cap_serial_k>; using lane_walker_narrow_t =
candidate_lane_walker<char, u16_t, uniform_substitution_costs_t, affine_gap_costs_t, sz_minimize_distance_k,
sz_similarity_global_k, sz_cap_haswell_k, (int)candidate_lanes_k,
void>; using lane_walker_wide_t =
candidate_lane_walker<char, u32_t, uniform_substitution_costs_t, affine_gap_costs_t, sz_minimize_distance_k,
sz_similarity_global_k, sz_cap_haswell_k, 8,
void>;
using scratch_allocator_t = typename std::allocator_traits<allocator_t>::template rebind_alloc<std::byte>;
using linear_fallback_t = levenshtein_distances<linear_gap_costs_t, allocator_t, capability_k>;
uniform_substitution_costs_t substituter_ {};
affine_gap_costs_t gap_costs_ {};
allocator_t alloc_ {};
safe_vector<std::byte, scratch_allocator_t> score_scratch_ {alloc_};
linear_fallback_t linear_fallback_;
levenshtein_distances(allocator_t alloc = {}) noexcept : alloc_(alloc), linear_fallback_(alloc) {}
levenshtein_distances(uniform_substitution_costs_t subs, affine_gap_costs_t gaps,
allocator_t alloc = allocator_t {}) noexcept
: substituter_(subs), gap_costs_(gaps), alloc_(alloc),
linear_fallback_(subs, linear_gap_costs_t {gaps.open}, alloc) {}
#pragma region Public Cross Product Overloads
template <typename queries_type_, typename candidates_type_, typename value_type_>
SZ_NOIPA status_t operator()(queries_type_ const &queries, candidates_type_ const &candidates,
strided_rows<value_type_> results, cpu_specs_t const &specs = {}) noexcept {
return cross_(queries, candidates, results, cross_similarities_t::all_pairs_k, specs);
}
template <typename queries_type_, typename candidates_type_, typename value_type_, typename executor_type_>
SZ_NOIPA status_t operator()(queries_type_ const &queries, candidates_type_ const &candidates,
strided_rows<value_type_> results, executor_type_ &&executor,
cpu_specs_t const &specs = {}) noexcept {
return cross_parallel_(queries, candidates, results, cross_similarities_t::all_pairs_k,
std::forward<executor_type_>(executor), specs);
}
template <typename sequences_type_, typename value_type_>
SZ_NOIPA status_t operator()(sequences_type_ const &sequences, strided_rows<value_type_> results,
cpu_specs_t const &specs = {}) noexcept {
return cross_(sequences, sequences, results, cross_similarities_t::symmetric_k, specs);
}
template <typename sequences_type_, typename value_type_, typename executor_type_>
SZ_NOIPA status_t operator()(sequences_type_ const &sequences, strided_rows<value_type_> results,
executor_type_ &&executor, cpu_specs_t const &specs = {}) noexcept {
return cross_parallel_(sequences, sequences, results, cross_similarities_t::symmetric_k,
std::forward<executor_type_>(executor), specs);
}
#pragma endregion Public Cross Product Overloads
#pragma region Cross Product Dispatch
private:
template <typename queries_type_, typename candidates_type_, typename value_type_>
status_t cross_(queries_type_ const &queries, candidates_type_ const &candidates, strided_rows<value_type_> results,
cross_similarities_t cross_kind, cpu_specs_t const &specs) noexcept {
if (gap_costs_.is_linear()) return linear_fallback_(queries, candidates, results, specs);
lane_walker_narrow_t narrow {substituter_, gap_costs_};
lane_walker_wide_t wide {substituter_, gap_costs_};
scoring_t fallback {substituter_, gap_costs_};
if (status_t status = score_scratch_.try_resize(
cross_product_candidate_lanes_scratch_(narrow, wide, fallback, queries, candidates, specs));
status != status_t::success_k)
return status;
return cross_product_candidate_lanes_range_(
narrow, wide, fallback, queries, candidates, results, cross_kind, 0,
cross_live_cells_count_(queries.size(), candidates.size(), cross_kind), scratch_space_t(score_scratch_),
specs);
}
template <typename queries_type_, typename candidates_type_, typename value_type_, typename executor_type_>
status_t cross_parallel_(queries_type_ const &queries, candidates_type_ const &candidates,
strided_rows<value_type_> results, cross_similarities_t cross_kind,
executor_type_ &&executor, cpu_specs_t const &specs) noexcept {
if (gap_costs_.is_linear())
return linear_fallback_(queries, candidates, results, std::forward<executor_type_>(executor), specs);
lane_walker_narrow_t narrow {substituter_, gap_costs_};
lane_walker_wide_t wide {substituter_, gap_costs_};
scoring_t fallback {substituter_, gap_costs_};
return cross_product_candidate_lanes_parallel_(narrow, wide, fallback, queries, candidates, results, cross_kind,
score_scratch_, std::forward<executor_type_>(executor), specs);
}
#pragma endregion Cross Product Dispatch
};
template <typename allocator_type_, sz_capability_t capability_>
struct levenshtein_distances_utf8<
linear_gap_costs_t, allocator_type_, capability_,
std::enable_if_t<(capability_ & sz_cap_haswell_k) != 0 && (capability_ & sz_cap_icelake_k) == 0>> {
using char_t = char;
using gap_costs_t = linear_gap_costs_t;
using allocator_t = allocator_type_;
using index_t = u32_t;
static constexpr sz_capability_t capability_k = capability_;
static constexpr size_t candidate_lanes_k = 16;
using scoring_t = levenshtein_distance_utf8<gap_costs_t, sz_cap_serial_k>; using myers_t = levenshtein_distance_myers<rune_t, capability_k>; using lane_walker_t =
candidate_lane_walker<rune_t, u16_t, uniform_substitution_costs_t, gap_costs_t, sz_minimize_distance_k,
sz_similarity_global_k, sz_cap_haswell_k, (int)candidate_lanes_k, void>;
using lane_walker_narrow_t = lane_walker_t; using lane_walker_wide_t =
candidate_lane_walker<rune_t, u32_t, uniform_substitution_costs_t, gap_costs_t, sz_minimize_distance_k,
sz_similarity_global_k, sz_cap_haswell_k, 8, void>; using rune_scoring_t = levenshtein_distance<rune_t, gap_costs_t, sz_cap_serial_k>; static constexpr index_t myers_lanes_k = myers_t::lanes_k;
using scratch_allocator_t = typename std::allocator_traits<allocator_t>::template rebind_alloc<std::byte>;
using rune_allocator_t = typename std::allocator_traits<allocator_t>::template rebind_alloc<rune_t>;
using rune_view_allocator_t =
typename std::allocator_traits<allocator_t>::template rebind_alloc<span<rune_t const>>;
using bytes_fallback_t = levenshtein_distances<linear_gap_costs_t, allocator_t, capability_k>;
uniform_substitution_costs_t substituter_ {};
linear_gap_costs_t gap_costs_ {};
allocator_t alloc_ {};
safe_vector<std::byte, scratch_allocator_t> score_scratch_ {alloc_};
safe_vector<rune_t, rune_allocator_t> query_arena_ {alloc_};
safe_vector<rune_t, rune_allocator_t> candidate_arena_ {alloc_};
safe_vector<span<rune_t const>, rune_view_allocator_t> query_runes_ {alloc_};
safe_vector<span<rune_t const>, rune_view_allocator_t> candidate_runes_ {alloc_};
bytes_fallback_t bytes_fallback_;
levenshtein_distances_utf8(allocator_t alloc = {}) noexcept : alloc_(alloc), bytes_fallback_(alloc) {}
levenshtein_distances_utf8(uniform_substitution_costs_t subs, linear_gap_costs_t gaps,
allocator_t alloc = allocator_t {}) noexcept
: substituter_(subs), gap_costs_(gaps), alloc_(alloc), bytes_fallback_(subs, gaps, alloc) {}
template <typename sequences_type_>
bool transcode_views_(sequences_type_ const &sequences, safe_vector<rune_t, rune_allocator_t> &arena,
safe_vector<span<rune_t const>, rune_view_allocator_t> &views) const noexcept {
size_t total_bytes = 0;
for (size_t index = 0; index < sequences.size(); ++index) total_bytes += to_view(sequences[index]).size();
if (arena.try_reserve(total_bytes) != status_t::success_k) return false;
if (arena.try_resize(0) != status_t::success_k) return false;
if (views.try_resize(sequences.size()) != status_t::success_k) return false;
for (size_t index = 0; index < sequences.size(); ++index) {
auto const bytes = to_view(sequences[index]);
size_t const rune_begin = arena.size();
rune_length_t rune_length;
for (size_t progress = 0; progress < bytes.size(); progress += rune_length) {
rune_t rune;
rune_length = sz_rune_decode_unchecked(bytes.data() + progress, &rune);
if (rune_length == sz_rune_invalid_k) return false;
if (arena.try_resize(arena.size() + 1) != status_t::success_k) return false;
arena[arena.size() - 1] = rune;
}
views[index] = span<rune_t const> {arena.data() + rune_begin, arena.size() - rune_begin};
}
return true;
}
size_t query_runes_bytes_(size_t byte_length, cpu_specs_t const &specs) const noexcept {
return round_up_to_multiple(sizeof(rune_t) * byte_length, specs.cache_line_width);
}
template <typename queries_type_, typename candidates_type_>
size_t worst_cell_scratch_(queries_type_ const &queries, candidates_type_ const &candidates,
cpu_specs_t const &specs) const noexcept {
size_t longest_query = 0, longest_query_index = 0, longest_candidate = 0, longest_candidate_index = 0;
for (size_t index = 0; index < queries.size(); ++index)
if (to_view(queries[index]).size() > longest_query)
longest_query = to_view(queries[index]).size(), longest_query_index = index;
for (size_t index = 0; index < candidates.size(); ++index)
if (to_view(candidates[index]).size() > longest_candidate)
longest_candidate = to_view(candidates[index]).size(), longest_candidate_index = index;
lane_walker_t lane_walker {substituter_, gap_costs_};
size_t const query_rune_bytes = query_runes_bytes_(longest_query, specs);
size_t const transpose_bytes = candidate_lanes_k * longest_candidate * sizeof(rune_t);
size_t const walker_scratch = longest_candidate ? lane_walker.scratch_space_needed(longest_candidate, specs)
: 0;
size_t const myers_transcode_bytes = round_up_to_multiple(
(size_t)myers_lanes_k * (longest_query + longest_candidate) * sizeof(rune_t), specs.cache_line_width);
size_t const myers_match_masks_bytes = myers_t::scratch_bytes_for(
sz_min_of_two(longest_query, longest_candidate));
size_t dp_scratch = 0;
if (queries.size() && candidates.size()) {
scoring_t dp {substituter_, gap_costs_};
dp_scratch = dp.scratch_space_needed(to_view(queries[longest_query_index]),
to_view(candidates[longest_candidate_index]), specs);
}
size_t const lane_walker_path = query_rune_bytes + transpose_bytes + walker_scratch;
size_t const myers_path = myers_transcode_bytes + myers_match_masks_bytes;
return sz_max_of_two(sz_max_of_two(lane_walker_path, myers_path), dp_scratch);
}
#pragma region Cross Product Scoring
static size_t transcode_runes_(span<char_t const> utf8, rune_t *runes_out) noexcept {
rune_length_t rune_length;
size_t rune_count = 0;
for (size_t progress_utf8 = 0; progress_utf8 < utf8.size(); progress_utf8 += rune_length, ++rune_count) {
rune_length = sz_rune_decode_unchecked(utf8.data() + progress_utf8, runes_out + rune_count);
if (rune_length == sz_rune_invalid_k) return SZ_SIZE_MAX;
}
return rune_count;
}
template <typename queries_type_, typename candidates_type_, typename results_type_>
SZ_NOINLINE status_t score_range_(queries_type_ const &queries, candidates_type_ const &candidates,
results_type_ &&results, cross_similarities_t cross_kind, size_t cell_begin,
size_t cell_end, scratch_space_t scratch, cpu_specs_t const &specs) noexcept {
using value_t = remove_cvref<decltype(results.data[0])>;
size_t const candidates_count = candidates.size();
size_t longest_query = 0, longest_candidate = 0;
for (size_t cell_index = cell_begin; cell_index != cell_end; ++cell_index) {
size_t query_index = 0, candidate_index = 0;
cross_cell_to_indices_(cell_index, candidates_count, cross_kind, query_index, candidate_index);
longest_query = sz_max_of_two(longest_query, to_view(queries[query_index]).size());
longest_candidate = sz_max_of_two(longest_candidate, to_view(candidates[candidate_index]).size());
}
size_t const transcode_bytes = round_up_to_multiple(
(size_t)myers_lanes_k * (longest_query + longest_candidate) * sizeof(rune_t), specs.cache_line_width);
rune_t *const rune_arena = reinterpret_cast<rune_t *>(scratch.data());
size_t const rune_arena_runes = transcode_bytes / sizeof(rune_t);
scratch_space_t const match_masks_scratch = transcode_bytes <= scratch.size()
? scratch.subspan(transcode_bytes,
scratch.size() - transcode_bytes)
: scratch_space_t {};
scratch_space_t const dp_scratch_space = scratch;
scoring_t dp {substituter_, gap_costs_};
dummy_executor_t dummy;
auto const destination_for = [&](size_t query_index, size_t candidate_index) noexcept {
cross_cell_destination_t<value_t> destination;
destination.primary = results.data + query_index * results.row_stride + candidate_index;
if (cross_kind == cross_similarities_t::symmetric_k && candidate_index != query_index)
destination.mirror = results.data + candidate_index * results.row_stride + query_index;
return destination;
};
auto const scatter = [&](cross_cell_destination_t<value_t> const &destination, size_t score) noexcept {
*destination.primary = static_cast<value_t>(score);
if (destination.mirror) *destination.mirror = static_cast<value_t>(score);
};
myers_t myers;
auto const transcode_cell = [&](span<char_t const> query, span<char_t const> candidate, size_t &arena_used,
span<rune_t const> &shorter_runes,
span<rune_t const> &longer_runes) noexcept -> bool {
size_t const query_offset = arena_used;
size_t query_runes_count = 0;
rune_length_t rune_length;
for (size_t progress = 0; progress < query.size(); progress += rune_length, ++query_runes_count) {
if (query_offset + query_runes_count >= rune_arena_runes) return false;
rune_length = sz_rune_decode_unchecked(query.data() + progress,
rune_arena + query_offset + query_runes_count);
if (rune_length == sz_rune_invalid_k) return false;
}
size_t const candidate_offset = query_offset + query_runes_count;
size_t candidate_runes_count = 0;
for (size_t progress = 0; progress < candidate.size(); progress += rune_length, ++candidate_runes_count) {
if (candidate_offset + candidate_runes_count >= rune_arena_runes) return false;
rune_length = sz_rune_decode_unchecked(candidate.data() + progress,
rune_arena + candidate_offset + candidate_runes_count);
if (rune_length == sz_rune_invalid_k) return false;
}
arena_used = candidate_offset + candidate_runes_count;
span<rune_t const> const query_view {rune_arena + query_offset, query_runes_count};
span<rune_t const> const candidate_view {rune_arena + candidate_offset, candidate_runes_count};
bool const query_is_shorter = query_runes_count <= candidate_runes_count;
shorter_runes = query_is_shorter ? query_view : candidate_view;
longer_runes = query_is_shorter ? candidate_view : query_view;
return true;
};
static constexpr size_t stack_words_capacity_k =
64; for (size_t cell_index = cell_begin; cell_index != cell_end;) {
size_t query_index = 0, candidate_index = 0;
cross_cell_to_indices_(cell_index, candidates_count, cross_kind, query_index, candidate_index);
auto const query = to_view(queries[query_index]);
auto const candidate = to_view(candidates[candidate_index]);
bool const fits_myers = query.size() != 0 && candidate.size() != 0 &&
sz_min_of_two(query.size(), candidate.size()) <= stack_words_capacity_k * 64;
if (!fits_myers || !match_masks_scratch.size()) {
size_t result_distance = 0;
if (status_t status = dp(query, candidate, result_distance, dp_scratch_space, dummy, specs);
status != status_t::success_k)
return status;
scatter(destination_for(query_index, candidate_index), result_distance);
++cell_index;
continue;
}
size_t arena_used = 0;
span<rune_t const> group_shorters[myers_lanes_k], group_longers[myers_lanes_k];
size_t group_positions[myers_lanes_k];
cross_cell_destination_t<value_t> group_destinations[myers_lanes_k];
size_t group_query_indices[myers_lanes_k], group_candidate_indices[myers_lanes_k];
span<rune_t const> seed_shorter, seed_longer;
if (!transcode_cell(query, candidate, arena_used, seed_shorter, seed_longer)) {
size_t result_distance = 0;
if (status_t status = dp(query, candidate, result_distance, dp_scratch_space, dummy, specs);
status != status_t::success_k)
return status;
scatter(destination_for(query_index, candidate_index), result_distance);
++cell_index;
continue;
}
size_t const seed_shorter_runes = seed_shorter.size();
if (seed_shorter_runes == 0) {
size_t result_distance = 0;
if (status_t status = dp(query, candidate, result_distance, dp_scratch_space, dummy, specs);
status != status_t::success_k)
return status;
scatter(destination_for(query_index, candidate_index), result_distance);
++cell_index;
continue;
}
size_t const seed_bucket = divide_round_up<size_t>(seed_shorter_runes, 64);
group_shorters[0] = seed_shorter;
group_longers[0] = seed_longer;
group_positions[0] = 0;
group_destinations[0] = destination_for(query_index, candidate_index);
group_query_indices[0] = query_index;
group_candidate_indices[0] = candidate_index;
index_t group = 1;
++cell_index;
for (; cell_index != cell_end && group != myers_lanes_k; ++cell_index) {
size_t next_query_index = 0, next_candidate_index = 0;
cross_cell_to_indices_(cell_index, candidates_count, cross_kind, next_query_index,
next_candidate_index);
auto const next_query = to_view(queries[next_query_index]);
auto const next_candidate = to_view(candidates[next_candidate_index]);
if (next_query.size() == 0 || next_candidate.size() == 0 ||
sz_min_of_two(next_query.size(), next_candidate.size()) > stack_words_capacity_k * 64)
break;
size_t const arena_before = arena_used;
span<rune_t const> next_shorter, next_longer;
if (!transcode_cell(next_query, next_candidate, arena_used, next_shorter, next_longer)) {
arena_used = arena_before; break;
}
if (next_shorter.size() == 0 || divide_round_up<size_t>(next_shorter.size(), 64) != seed_bucket) {
arena_used = arena_before; break;
}
group_shorters[group] = next_shorter;
group_longers[group] = next_longer;
group_positions[group] = group;
group_destinations[group] = destination_for(next_query_index, next_candidate_index);
group_query_indices[group] = next_query_index;
group_candidate_indices[group] = next_candidate_index;
++group;
}
cross_cell_writer_t<value_t> group_writer;
group_writer.destinations = group_destinations;
lane_pairs_view<rune_t> const group_pairs {{group_shorters, group},
{group_longers, group},
{group_positions, group}};
status_t status = dispatch_word_bucket_<1, 8>(
seed_bucket,
[&](auto bucket) {
if constexpr (bucket.value == 1)
return myers.distances_4x64_(group_pairs, group_writer, match_masks_scratch);
else
return myers.template distances_4x_multiword_<bucket.value>(group_pairs, group_writer,
match_masks_scratch);
},
[&] { return myers.distances_4x_multiword_large_(group_pairs, group_writer, match_masks_scratch); });
if (status == status_t::success_k) continue;
for (index_t lane = 0; lane != group; ++lane) {
size_t lane_score = 0;
auto const lane_query = to_view(queries[group_query_indices[lane]]);
auto const lane_candidate = to_view(candidates[group_candidate_indices[lane]]);
if (status_t lane_status = dp(lane_query, lane_candidate, lane_score, dp_scratch_space, dummy, specs);
lane_status != status_t::success_k)
return lane_status;
scatter(group_destinations[lane], lane_score);
}
}
return status_t::success_k;
}
template <typename queries_type_, typename candidates_type_, typename results_type_, typename executor_type_>
SZ_NOINLINE status_t score_parallel_(queries_type_ const &queries, candidates_type_ const &candidates,
results_type_ &&results, cross_similarities_t cross_kind,
executor_type_ &&executor, cpu_specs_t const &specs) noexcept {
size_t const cells_count = cross_live_cells_count_(queries.size(), candidates.size(), cross_kind);
size_t const worker_scratch = worst_cell_scratch_(queries, candidates, specs);
size_t const workers = sz_max_of_two(executor.threads_count(), (size_t)1);
if (status_t status = score_scratch_.try_resize(worker_scratch * workers); status != status_t::success_k)
return status;
using prong_t = typename remove_cvref<executor_type_>::prong_t;
schedule_batches_t const schedule_batches = schedule_batches_(cells_count, workers,
lockstep_lanes_of_<myers_t>::value);
atomic_status_t status;
executor.for_n_dynamic(schedule_batches.batches_count, [&](prong_t prong) noexcept {
if (status != status_t::success_k) return;
scratch_space_t slice =
scratch_space_t(score_scratch_).subspan(prong.thread * worker_scratch, worker_scratch);
status = score_range_(queries, candidates, results, cross_kind, schedule_batches.batch_begin(prong.task),
schedule_batches.batch_end(prong.task, cells_count), slice, specs);
});
return status;
}
#pragma endregion Cross Product Scoring
#pragma region Non Unit Cross Product via Rune Lane Driver
template <typename queries_type_, typename candidates_type_, typename results_type_>
status_t cross_via_lanes_(queries_type_ const &queries, candidates_type_ const &candidates, results_type_ &&results,
cross_similarities_t cross_kind, cpu_specs_t const &specs) noexcept {
bool const same = static_cast<void const *>(&queries) == static_cast<void const *>(&candidates);
if (!transcode_views_(queries, query_arena_, query_runes_) ||
(!same && !transcode_views_(candidates, candidate_arena_, candidate_runes_)))
return cross_sequentially_<size_t>(scoring_t {substituter_, gap_costs_}, queries, candidates, results,
cross_kind, score_scratch_, specs);
auto const &candidate_views = same ? query_runes_ : candidate_runes_;
lane_walker_narrow_t narrow {substituter_, gap_costs_};
lane_walker_wide_t wide {substituter_, gap_costs_};
rune_scoring_t fallback {substituter_, gap_costs_};
if (status_t status = score_scratch_.try_resize(
cross_product_candidate_lanes_scratch_(narrow, wide, fallback, query_runes_, candidate_views, specs));
status != status_t::success_k)
return status;
return cross_product_candidate_lanes_range_(
narrow, wide, fallback, query_runes_, candidate_views, results, cross_kind, 0,
cross_live_cells_count_(query_runes_.size(), candidate_views.size(), cross_kind),
scratch_space_t(score_scratch_), specs);
}
template <typename queries_type_, typename candidates_type_, typename results_type_, typename executor_type_>
status_t cross_via_lanes_parallel_(queries_type_ const &queries, candidates_type_ const &candidates,
results_type_ &&results, cross_similarities_t cross_kind,
executor_type_ &&executor, cpu_specs_t const &specs) noexcept {
bool const same = static_cast<void const *>(&queries) == static_cast<void const *>(&candidates);
if (!transcode_views_(queries, query_arena_, query_runes_) ||
(!same && !transcode_views_(candidates, candidate_arena_, candidate_runes_)))
return cross_in_parallel_<size_t>(scoring_t {substituter_, gap_costs_}, queries, candidates, results,
cross_kind, score_scratch_, std::forward<executor_type_>(executor),
specs);
auto const &candidate_views = same ? query_runes_ : candidate_runes_;
lane_walker_narrow_t narrow {substituter_, gap_costs_};
lane_walker_wide_t wide {substituter_, gap_costs_};
rune_scoring_t fallback {substituter_, gap_costs_};
return cross_product_candidate_lanes_parallel_(narrow, wide, fallback, query_runes_, candidate_views, results,
cross_kind, score_scratch_,
std::forward<executor_type_>(executor), specs);
}
#pragma endregion Non Unit Cross Product via Rune Lane Driver
#pragma region Public Cross Product Overloads
template <typename queries_type_, typename candidates_type_, typename value_type_>
SZ_NOIPA status_t operator()(queries_type_ const &queries, candidates_type_ const &candidates,
strided_rows<value_type_> results, cpu_specs_t const &specs = {}) noexcept {
return cross_(queries, candidates, results, cross_similarities_t::all_pairs_k, specs);
}
template <typename queries_type_, typename candidates_type_, typename value_type_, typename executor_type_>
SZ_NOIPA status_t operator()(queries_type_ const &queries, candidates_type_ const &candidates,
strided_rows<value_type_> results, executor_type_ &&executor,
cpu_specs_t const &specs = {}) noexcept {
return cross_parallel_(queries, candidates, results, cross_similarities_t::all_pairs_k,
std::forward<executor_type_>(executor), specs);
}
template <typename sequences_type_, typename value_type_>
SZ_NOIPA status_t operator()(sequences_type_ const &sequences, strided_rows<value_type_> results,
cpu_specs_t const &specs = {}) noexcept {
return cross_(sequences, sequences, results, cross_similarities_t::symmetric_k, specs);
}
template <typename sequences_type_, typename value_type_, typename executor_type_>
SZ_NOIPA status_t operator()(sequences_type_ const &sequences, strided_rows<value_type_> results,
executor_type_ &&executor, cpu_specs_t const &specs = {}) noexcept {
return cross_parallel_(sequences, sequences, results, cross_similarities_t::symmetric_k,
std::forward<executor_type_>(executor), specs);
}
#pragma endregion Public Cross Product Overloads
#pragma region Cross Product Dispatch
private:
template <typename queries_type_, typename candidates_type_, typename value_type_>
status_t cross_(queries_type_ const &queries, candidates_type_ const &candidates, strided_rows<value_type_> results,
cross_similarities_t cross_kind, cpu_specs_t const &specs) noexcept {
if (corpus_is_ascii_<sz_find_byteset_haswell>(queries) && corpus_is_ascii_<sz_find_byteset_haswell>(candidates))
return bytes_fallback_(queries, candidates, results, specs);
if (!is_unit_cost(substituter_, gap_costs_))
return cross_via_lanes_(queries, candidates, results, cross_kind, specs);
if (status_t status = score_scratch_.try_resize(worst_cell_scratch_(queries, candidates, specs));
status != status_t::success_k)
return status;
return score_range_(queries, candidates, results, cross_kind, 0,
cross_live_cells_count_(queries.size(), candidates.size(), cross_kind),
scratch_space_t(score_scratch_), specs);
}
template <typename queries_type_, typename candidates_type_, typename value_type_, typename executor_type_>
status_t cross_parallel_(queries_type_ const &queries, candidates_type_ const &candidates,
strided_rows<value_type_> results, cross_similarities_t cross_kind,
executor_type_ &&executor, cpu_specs_t const &specs) noexcept {
if (corpus_is_ascii_<sz_find_byteset_haswell>(queries) && corpus_is_ascii_<sz_find_byteset_haswell>(candidates))
return bytes_fallback_(queries, candidates, results, std::forward<executor_type_>(executor), specs);
if (!is_unit_cost(substituter_, gap_costs_))
return cross_via_lanes_parallel_(queries, candidates, results, cross_kind,
std::forward<executor_type_>(executor), specs);
return score_parallel_(queries, candidates, results, cross_kind, std::forward<executor_type_>(executor), specs);
}
#pragma endregion Cross Product Dispatch
};
template <typename allocator_type_, sz_capability_t capability_>
struct levenshtein_distances_utf8<
affine_gap_costs_t, allocator_type_, capability_,
std::enable_if_t<(capability_ & sz_cap_haswell_k) != 0 && (capability_ & sz_cap_icelake_k) == 0>> {
using char_t = char;
using gap_costs_t = affine_gap_costs_t;
using allocator_t = allocator_type_;
using index_t = u32_t;
static constexpr sz_capability_t capability_k = capability_;
static constexpr size_t candidate_lanes_k = 16;
using scoring_t = levenshtein_distance_utf8<gap_costs_t, sz_cap_serial_k>; using lane_walker_narrow_t =
candidate_lane_walker<rune_t, u16_t, uniform_substitution_costs_t, affine_gap_costs_t, sz_minimize_distance_k,
sz_similarity_global_k, sz_cap_haswell_k, (int)candidate_lanes_k,
void>; using lane_walker_wide_t =
candidate_lane_walker<rune_t, u32_t, uniform_substitution_costs_t, affine_gap_costs_t, sz_minimize_distance_k,
sz_similarity_global_k, sz_cap_haswell_k, 8, void>; using rune_scoring_t = levenshtein_distance<rune_t, gap_costs_t, sz_cap_serial_k>;
using scratch_allocator_t = typename std::allocator_traits<allocator_t>::template rebind_alloc<std::byte>;
using rune_allocator_t = typename std::allocator_traits<allocator_t>::template rebind_alloc<rune_t>;
using rune_view_allocator_t =
typename std::allocator_traits<allocator_t>::template rebind_alloc<span<rune_t const>>;
using linear_fallback_t = levenshtein_distances_utf8<linear_gap_costs_t, allocator_t, capability_k>;
using bytes_fallback_t = levenshtein_distances<affine_gap_costs_t, allocator_t, capability_k>;
uniform_substitution_costs_t substituter_ {};
affine_gap_costs_t gap_costs_ {};
allocator_t alloc_ {};
safe_vector<std::byte, scratch_allocator_t> score_scratch_ {alloc_};
safe_vector<rune_t, rune_allocator_t> query_arena_ {alloc_};
safe_vector<rune_t, rune_allocator_t> candidate_arena_ {alloc_};
safe_vector<span<rune_t const>, rune_view_allocator_t> query_runes_ {alloc_};
safe_vector<span<rune_t const>, rune_view_allocator_t> candidate_runes_ {alloc_};
linear_fallback_t linear_fallback_;
bytes_fallback_t bytes_fallback_;
levenshtein_distances_utf8(allocator_t alloc = {}) noexcept
: alloc_(alloc), linear_fallback_(alloc), bytes_fallback_(alloc) {}
levenshtein_distances_utf8(uniform_substitution_costs_t subs, affine_gap_costs_t gaps,
allocator_t alloc = allocator_t {}) noexcept
: substituter_(subs), gap_costs_(gaps), alloc_(alloc),
linear_fallback_(subs, linear_gap_costs_t {gaps.open}, alloc), bytes_fallback_(subs, gaps, alloc) {}
template <typename sequences_type_>
bool transcode_views_(sequences_type_ const &sequences, safe_vector<rune_t, rune_allocator_t> &arena,
safe_vector<span<rune_t const>, rune_view_allocator_t> &views) const noexcept {
size_t total_bytes = 0;
for (size_t index = 0; index < sequences.size(); ++index) total_bytes += to_view(sequences[index]).size();
if (arena.try_reserve(total_bytes) != status_t::success_k) return false;
if (arena.try_resize(0) != status_t::success_k) return false;
if (views.try_resize(sequences.size()) != status_t::success_k) return false;
for (size_t index = 0; index < sequences.size(); ++index) {
auto const bytes = to_view(sequences[index]);
size_t const rune_begin = arena.size();
rune_length_t rune_length;
for (size_t progress = 0; progress < bytes.size(); progress += rune_length) {
rune_t rune;
rune_length = sz_rune_decode_unchecked(bytes.data() + progress, &rune);
if (rune_length == sz_rune_invalid_k) return false;
if (arena.try_resize(arena.size() + 1) != status_t::success_k) return false;
arena[arena.size() - 1] = rune;
}
views[index] = span<rune_t const> {arena.data() + rune_begin, arena.size() - rune_begin};
}
return true;
}
#pragma region Cross Product via Lane Driver
template <typename queries_type_, typename candidates_type_, typename results_type_>
status_t cross_via_lanes_(queries_type_ const &queries, candidates_type_ const &candidates, results_type_ &&results,
cross_similarities_t cross_kind, cpu_specs_t const &specs) noexcept {
if (gap_costs_.is_linear()) return linear_fallback_(queries, candidates, results, specs);
if (corpus_is_ascii_<sz_find_byteset_haswell>(queries) && corpus_is_ascii_<sz_find_byteset_haswell>(candidates))
return bytes_fallback_(queries, candidates, results, specs);
bool const same = static_cast<void const *>(&queries) == static_cast<void const *>(&candidates);
if (!transcode_views_(queries, query_arena_, query_runes_) ||
(!same && !transcode_views_(candidates, candidate_arena_, candidate_runes_)))
return cross_sequentially_<size_t>(scoring_t {substituter_, gap_costs_}, queries, candidates, results,
cross_kind, score_scratch_, specs);
auto const &candidate_views = same ? query_runes_ : candidate_runes_;
lane_walker_narrow_t narrow {substituter_, gap_costs_};
lane_walker_wide_t wide {substituter_, gap_costs_};
rune_scoring_t fallback {substituter_, gap_costs_};
if (status_t status = score_scratch_.try_resize(
cross_product_candidate_lanes_scratch_(narrow, wide, fallback, query_runes_, candidate_views, specs));
status != status_t::success_k)
return status;
return cross_product_candidate_lanes_range_(
narrow, wide, fallback, query_runes_, candidate_views, results, cross_kind, 0,
cross_live_cells_count_(query_runes_.size(), candidate_views.size(), cross_kind),
scratch_space_t(score_scratch_), specs);
}
template <typename queries_type_, typename candidates_type_, typename results_type_, typename executor_type_>
status_t cross_via_lanes_parallel_(queries_type_ const &queries, candidates_type_ const &candidates,
results_type_ &&results, cross_similarities_t cross_kind,
executor_type_ &&executor, cpu_specs_t const &specs) noexcept {
if (gap_costs_.is_linear())
return linear_fallback_(queries, candidates, results, std::forward<executor_type_>(executor), specs);
if (corpus_is_ascii_<sz_find_byteset_haswell>(queries) && corpus_is_ascii_<sz_find_byteset_haswell>(candidates))
return bytes_fallback_(queries, candidates, results, std::forward<executor_type_>(executor), specs);
bool const same = static_cast<void const *>(&queries) == static_cast<void const *>(&candidates);
if (!transcode_views_(queries, query_arena_, query_runes_) ||
(!same && !transcode_views_(candidates, candidate_arena_, candidate_runes_)))
return cross_in_parallel_<size_t>(scoring_t {substituter_, gap_costs_}, queries, candidates, results,
cross_kind, score_scratch_, std::forward<executor_type_>(executor),
specs);
auto const &candidate_views = same ? query_runes_ : candidate_runes_;
lane_walker_narrow_t narrow {substituter_, gap_costs_};
lane_walker_wide_t wide {substituter_, gap_costs_};
rune_scoring_t fallback {substituter_, gap_costs_};
return cross_product_candidate_lanes_parallel_(narrow, wide, fallback, query_runes_, candidate_views, results,
cross_kind, score_scratch_,
std::forward<executor_type_>(executor), specs);
}
#pragma endregion Cross Product via Lane Driver
#pragma region Public Cross Product Overloads
template <typename queries_type_, typename candidates_type_, typename value_type_>
SZ_NOIPA status_t operator()(queries_type_ const &queries, candidates_type_ const &candidates,
strided_rows<value_type_> results, cpu_specs_t const &specs = {}) noexcept {
return cross_via_lanes_(queries, candidates, results, cross_similarities_t::all_pairs_k, specs);
}
template <typename queries_type_, typename candidates_type_, typename value_type_, typename executor_type_>
SZ_NOIPA status_t operator()(queries_type_ const &queries, candidates_type_ const &candidates,
strided_rows<value_type_> results, executor_type_ &&executor,
cpu_specs_t const &specs = {}) noexcept {
return cross_via_lanes_parallel_(queries, candidates, results, cross_similarities_t::all_pairs_k,
std::forward<executor_type_>(executor), specs);
}
template <typename sequences_type_, typename value_type_>
SZ_NOIPA status_t operator()(sequences_type_ const &sequences, strided_rows<value_type_> results,
cpu_specs_t const &specs = {}) noexcept {
return cross_via_lanes_(sequences, sequences, results, cross_similarities_t::symmetric_k, specs);
}
template <typename sequences_type_, typename value_type_, typename executor_type_>
SZ_NOIPA status_t operator()(sequences_type_ const &sequences, strided_rows<value_type_> results,
executor_type_ &&executor, cpu_specs_t const &specs = {}) noexcept {
return cross_via_lanes_parallel_(sequences, sequences, results, cross_similarities_t::symmetric_k,
std::forward<executor_type_>(executor), specs);
}
#pragma endregion Public Cross Product Overloads
};
template <typename allocator_type_, sz_capability_t capability_>
struct needleman_wunsch_scores<
error_costs_32x32_t, linear_gap_costs_t, allocator_type_, capability_,
std::enable_if_t<(capability_ & sz_cap_haswell_k) != 0 && (capability_ & sz_cap_icelake_k) == 0>> {
using char_t = char;
using substituter_t = error_costs_32x32_t;
using gap_costs_t = linear_gap_costs_t;
using allocator_t = allocator_type_;
using index_t = u32_t;
static constexpr sz_capability_t capability_k = capability_;
static constexpr size_t candidate_lanes_k = 16;
using scoring_t = needleman_wunsch_score<char, substituter_t, gap_costs_t, sz_caps_sh_k>; using lane_walker_narrow_t =
candidate_lane_walker<char, i16_t, substituter_t, gap_costs_t, sz_maximize_score_k, sz_similarity_global_k,
sz_cap_haswell_k, (int)candidate_lanes_k, void>; using lane_walker_wide_t =
candidate_lane_walker<char, i32_t, substituter_t, gap_costs_t, sz_maximize_score_k, sz_similarity_global_k,
sz_cap_haswell_k, 8, void>;
using scratch_allocator_t = typename std::allocator_traits<allocator_t>::template rebind_alloc<std::byte>;
substituter_t substituter_ {};
linear_gap_costs_t gap_costs_ {};
allocator_t alloc_ {};
safe_vector<std::byte, scratch_allocator_t> score_scratch_ {alloc_};
needleman_wunsch_scores(allocator_t alloc = {}) noexcept : alloc_(alloc) {}
needleman_wunsch_scores(substituter_t subs, linear_gap_costs_t gaps, allocator_t alloc = allocator_t {}) noexcept
: substituter_(subs), gap_costs_(gaps), alloc_(alloc) {}
#pragma region Public Cross Product Overloads
template <typename queries_type_, typename candidates_type_, typename value_type_>
SZ_NOIPA status_t operator()(queries_type_ const &queries, candidates_type_ const &candidates,
strided_rows<value_type_> results, cpu_specs_t const &specs = {}) noexcept {
lane_walker_narrow_t narrow {substituter_, gap_costs_};
lane_walker_wide_t wide {substituter_, gap_costs_};
scoring_t fallback {substituter_, gap_costs_};
if (status_t status = score_scratch_.try_resize(
cross_product_candidate_lanes_scratch_(narrow, wide, fallback, queries, candidates, specs));
status != status_t::success_k)
return status;
return cross_product_candidate_lanes_range_(
narrow, wide, fallback, queries, candidates, results, cross_similarities_t::all_pairs_k, 0,
cross_live_cells_count_(queries.size(), candidates.size(), cross_similarities_t::all_pairs_k),
scratch_space_t(score_scratch_), specs);
}
template <typename queries_type_, typename candidates_type_, typename value_type_, typename executor_type_>
SZ_NOIPA status_t operator()(queries_type_ const &queries, candidates_type_ const &candidates,
strided_rows<value_type_> results, executor_type_ &&executor,
cpu_specs_t const &specs = {}) noexcept {
lane_walker_narrow_t narrow {substituter_, gap_costs_};
lane_walker_wide_t wide {substituter_, gap_costs_};
scoring_t fallback {substituter_, gap_costs_};
return cross_product_candidate_lanes_parallel_(narrow, wide, fallback, queries, candidates, results,
cross_similarities_t::all_pairs_k, score_scratch_,
std::forward<executor_type_>(executor), specs);
}
template <typename sequences_type_, typename value_type_>
SZ_NOIPA status_t operator()(sequences_type_ const &sequences, strided_rows<value_type_> results,
cpu_specs_t const &specs = {}) noexcept {
lane_walker_narrow_t narrow {substituter_, gap_costs_};
lane_walker_wide_t wide {substituter_, gap_costs_};
scoring_t fallback {substituter_, gap_costs_};
if (status_t status = score_scratch_.try_resize(
cross_product_candidate_lanes_scratch_(narrow, wide, fallback, sequences, sequences, specs));
status != status_t::success_k)
return status;
return cross_product_candidate_lanes_range_(
narrow, wide, fallback, sequences, sequences, results, cross_similarities_t::symmetric_k, 0,
cross_live_cells_count_(sequences.size(), sequences.size(), cross_similarities_t::symmetric_k),
scratch_space_t(score_scratch_), specs);
}
template <typename sequences_type_, typename value_type_, typename executor_type_>
SZ_NOIPA status_t operator()(sequences_type_ const &sequences, strided_rows<value_type_> results,
executor_type_ &&executor, cpu_specs_t const &specs = {}) noexcept {
lane_walker_narrow_t narrow {substituter_, gap_costs_};
lane_walker_wide_t wide {substituter_, gap_costs_};
scoring_t fallback {substituter_, gap_costs_};
return cross_product_candidate_lanes_parallel_(narrow, wide, fallback, sequences, sequences, results,
cross_similarities_t::symmetric_k, score_scratch_,
std::forward<executor_type_>(executor), specs);
}
#pragma endregion Public Cross Product Overloads
};
template <typename allocator_type_, sz_capability_t capability_>
struct smith_waterman_scores<
error_costs_32x32_t, linear_gap_costs_t, allocator_type_, capability_,
std::enable_if_t<(capability_ & sz_cap_haswell_k) != 0 && (capability_ & sz_cap_icelake_k) == 0>> {
using char_t = char;
using substituter_t = error_costs_32x32_t;
using gap_costs_t = linear_gap_costs_t;
using allocator_t = allocator_type_;
using index_t = u32_t;
static constexpr sz_capability_t capability_k = capability_;
static constexpr size_t candidate_lanes_k = 16;
using scoring_t = smith_waterman_score<char, substituter_t, gap_costs_t, sz_caps_sh_k>; using lane_walker_narrow_t =
candidate_lane_walker<char, i16_t, substituter_t, gap_costs_t, sz_maximize_score_k, sz_similarity_local_k,
sz_cap_haswell_k, (int)candidate_lanes_k, void>; using lane_walker_wide_t =
candidate_lane_walker<char, i32_t, substituter_t, gap_costs_t, sz_maximize_score_k, sz_similarity_local_k,
sz_cap_haswell_k, 8, void>;
using scratch_allocator_t = typename std::allocator_traits<allocator_t>::template rebind_alloc<std::byte>;
substituter_t substituter_ {};
linear_gap_costs_t gap_costs_ {};
allocator_t alloc_ {};
safe_vector<std::byte, scratch_allocator_t> score_scratch_ {alloc_};
smith_waterman_scores(allocator_t alloc = {}) noexcept : alloc_(alloc) {}
smith_waterman_scores(substituter_t subs, linear_gap_costs_t gaps, allocator_t alloc = allocator_t {}) noexcept
: substituter_(subs), gap_costs_(gaps), alloc_(alloc) {}
#pragma region Public Cross Product Overloads
template <typename queries_type_, typename candidates_type_, typename value_type_>
SZ_NOIPA status_t operator()(queries_type_ const &queries, candidates_type_ const &candidates,
strided_rows<value_type_> results, cpu_specs_t const &specs = {}) noexcept {
lane_walker_narrow_t narrow {substituter_, gap_costs_};
lane_walker_wide_t wide {substituter_, gap_costs_};
scoring_t fallback {substituter_, gap_costs_};
if (status_t status = score_scratch_.try_resize(
cross_product_candidate_lanes_scratch_(narrow, wide, fallback, queries, candidates, specs));
status != status_t::success_k)
return status;
return cross_product_candidate_lanes_range_(
narrow, wide, fallback, queries, candidates, results, cross_similarities_t::all_pairs_k, 0,
cross_live_cells_count_(queries.size(), candidates.size(), cross_similarities_t::all_pairs_k),
scratch_space_t(score_scratch_), specs);
}
template <typename queries_type_, typename candidates_type_, typename value_type_, typename executor_type_>
SZ_NOIPA status_t operator()(queries_type_ const &queries, candidates_type_ const &candidates,
strided_rows<value_type_> results, executor_type_ &&executor,
cpu_specs_t const &specs = {}) noexcept {
lane_walker_narrow_t narrow {substituter_, gap_costs_};
lane_walker_wide_t wide {substituter_, gap_costs_};
scoring_t fallback {substituter_, gap_costs_};
return cross_product_candidate_lanes_parallel_(narrow, wide, fallback, queries, candidates, results,
cross_similarities_t::all_pairs_k, score_scratch_,
std::forward<executor_type_>(executor), specs);
}
template <typename sequences_type_, typename value_type_>
SZ_NOIPA status_t operator()(sequences_type_ const &sequences, strided_rows<value_type_> results,
cpu_specs_t const &specs = {}) noexcept {
lane_walker_narrow_t narrow {substituter_, gap_costs_};
lane_walker_wide_t wide {substituter_, gap_costs_};
scoring_t fallback {substituter_, gap_costs_};
if (status_t status = score_scratch_.try_resize(
cross_product_candidate_lanes_scratch_(narrow, wide, fallback, sequences, sequences, specs));
status != status_t::success_k)
return status;
return cross_product_candidate_lanes_range_(
narrow, wide, fallback, sequences, sequences, results, cross_similarities_t::symmetric_k, 0,
cross_live_cells_count_(sequences.size(), sequences.size(), cross_similarities_t::symmetric_k),
scratch_space_t(score_scratch_), specs);
}
template <typename sequences_type_, typename value_type_, typename executor_type_>
SZ_NOIPA status_t operator()(sequences_type_ const &sequences, strided_rows<value_type_> results,
executor_type_ &&executor, cpu_specs_t const &specs = {}) noexcept {
lane_walker_narrow_t narrow {substituter_, gap_costs_};
lane_walker_wide_t wide {substituter_, gap_costs_};
scoring_t fallback {substituter_, gap_costs_};
return cross_product_candidate_lanes_parallel_(narrow, wide, fallback, sequences, sequences, results,
cross_similarities_t::symmetric_k, score_scratch_,
std::forward<executor_type_>(executor), specs);
}
#pragma endregion Public Cross Product Overloads
};
template <typename allocator_type_, sz_capability_t capability_>
struct needleman_wunsch_scores<
error_costs_32x32_t, affine_gap_costs_t, allocator_type_, capability_,
std::enable_if_t<(capability_ & sz_cap_haswell_k) != 0 && (capability_ & sz_cap_icelake_k) == 0>> {
using char_t = char;
using substituter_t = error_costs_32x32_t;
using gap_costs_t = affine_gap_costs_t;
using allocator_t = allocator_type_;
using index_t = u32_t;
static constexpr sz_capability_t capability_k = capability_;
static constexpr size_t candidate_lanes_k = 16;
using scoring_t = needleman_wunsch_score<char, substituter_t, gap_costs_t, sz_caps_sh_k>; using lane_walker_narrow_t =
candidate_lane_walker<char, i16_t, substituter_t, gap_costs_t, sz_maximize_score_k, sz_similarity_global_k,
sz_cap_haswell_k, (int)candidate_lanes_k, void>; using lane_walker_wide_t =
candidate_lane_walker<char, i32_t, substituter_t, gap_costs_t, sz_maximize_score_k, sz_similarity_global_k,
sz_cap_haswell_k, 8, void>;
using scratch_allocator_t = typename std::allocator_traits<allocator_t>::template rebind_alloc<std::byte>;
substituter_t substituter_ {};
affine_gap_costs_t gap_costs_ {};
allocator_t alloc_ {};
safe_vector<std::byte, scratch_allocator_t> score_scratch_ {alloc_};
needleman_wunsch_scores(allocator_t alloc = {}) noexcept : alloc_(alloc) {}
needleman_wunsch_scores(substituter_t subs, affine_gap_costs_t gaps, allocator_t alloc = allocator_t {}) noexcept
: substituter_(subs), gap_costs_(gaps), alloc_(alloc) {}
#pragma region Public Cross Product Overloads
template <typename queries_type_, typename candidates_type_, typename value_type_>
SZ_NOIPA status_t operator()(queries_type_ const &queries, candidates_type_ const &candidates,
strided_rows<value_type_> results, cpu_specs_t const &specs = {}) noexcept {
lane_walker_narrow_t narrow {substituter_, gap_costs_};
lane_walker_wide_t wide {substituter_, gap_costs_};
scoring_t fallback {substituter_, gap_costs_};
if (status_t status = score_scratch_.try_resize(
cross_product_candidate_lanes_scratch_(narrow, wide, fallback, queries, candidates, specs));
status != status_t::success_k)
return status;
return cross_product_candidate_lanes_range_(
narrow, wide, fallback, queries, candidates, results, cross_similarities_t::all_pairs_k, 0,
cross_live_cells_count_(queries.size(), candidates.size(), cross_similarities_t::all_pairs_k),
scratch_space_t(score_scratch_), specs);
}
template <typename queries_type_, typename candidates_type_, typename value_type_, typename executor_type_>
SZ_NOIPA status_t operator()(queries_type_ const &queries, candidates_type_ const &candidates,
strided_rows<value_type_> results, executor_type_ &&executor,
cpu_specs_t const &specs = {}) noexcept {
lane_walker_narrow_t narrow {substituter_, gap_costs_};
lane_walker_wide_t wide {substituter_, gap_costs_};
scoring_t fallback {substituter_, gap_costs_};
return cross_product_candidate_lanes_parallel_(narrow, wide, fallback, queries, candidates, results,
cross_similarities_t::all_pairs_k, score_scratch_,
std::forward<executor_type_>(executor), specs);
}
template <typename sequences_type_, typename value_type_>
SZ_NOIPA status_t operator()(sequences_type_ const &sequences, strided_rows<value_type_> results,
cpu_specs_t const &specs = {}) noexcept {
lane_walker_narrow_t narrow {substituter_, gap_costs_};
lane_walker_wide_t wide {substituter_, gap_costs_};
scoring_t fallback {substituter_, gap_costs_};
if (status_t status = score_scratch_.try_resize(
cross_product_candidate_lanes_scratch_(narrow, wide, fallback, sequences, sequences, specs));
status != status_t::success_k)
return status;
return cross_product_candidate_lanes_range_(
narrow, wide, fallback, sequences, sequences, results, cross_similarities_t::symmetric_k, 0,
cross_live_cells_count_(sequences.size(), sequences.size(), cross_similarities_t::symmetric_k),
scratch_space_t(score_scratch_), specs);
}
template <typename sequences_type_, typename value_type_, typename executor_type_>
SZ_NOIPA status_t operator()(sequences_type_ const &sequences, strided_rows<value_type_> results,
executor_type_ &&executor, cpu_specs_t const &specs = {}) noexcept {
lane_walker_narrow_t narrow {substituter_, gap_costs_};
lane_walker_wide_t wide {substituter_, gap_costs_};
scoring_t fallback {substituter_, gap_costs_};
return cross_product_candidate_lanes_parallel_(narrow, wide, fallback, sequences, sequences, results,
cross_similarities_t::symmetric_k, score_scratch_,
std::forward<executor_type_>(executor), specs);
}
#pragma endregion Public Cross Product Overloads
};
template <typename allocator_type_, sz_capability_t capability_>
struct smith_waterman_scores<
error_costs_32x32_t, affine_gap_costs_t, allocator_type_, capability_,
std::enable_if_t<(capability_ & sz_cap_haswell_k) != 0 && (capability_ & sz_cap_icelake_k) == 0>> {
using char_t = char;
using substituter_t = error_costs_32x32_t;
using gap_costs_t = affine_gap_costs_t;
using allocator_t = allocator_type_;
using index_t = u32_t;
static constexpr sz_capability_t capability_k = capability_;
static constexpr size_t candidate_lanes_k = 16;
using scoring_t = smith_waterman_score<char, substituter_t, gap_costs_t, sz_caps_sh_k>; using lane_walker_narrow_t =
candidate_lane_walker<char, i16_t, substituter_t, gap_costs_t, sz_maximize_score_k, sz_similarity_local_k,
sz_cap_haswell_k, (int)candidate_lanes_k, void>; using lane_walker_wide_t =
candidate_lane_walker<char, i32_t, substituter_t, gap_costs_t, sz_maximize_score_k, sz_similarity_local_k,
sz_cap_haswell_k, 8, void>;
using scratch_allocator_t = typename std::allocator_traits<allocator_t>::template rebind_alloc<std::byte>;
substituter_t substituter_ {};
affine_gap_costs_t gap_costs_ {};
allocator_t alloc_ {};
safe_vector<std::byte, scratch_allocator_t> score_scratch_ {alloc_};
smith_waterman_scores(allocator_t alloc = {}) noexcept : alloc_(alloc) {}
smith_waterman_scores(substituter_t subs, affine_gap_costs_t gaps, allocator_t alloc = allocator_t {}) noexcept
: substituter_(subs), gap_costs_(gaps), alloc_(alloc) {}
#pragma region Public Cross Product Overloads
template <typename queries_type_, typename candidates_type_, typename value_type_>
SZ_NOIPA status_t operator()(queries_type_ const &queries, candidates_type_ const &candidates,
strided_rows<value_type_> results, cpu_specs_t const &specs = {}) noexcept {
lane_walker_narrow_t narrow {substituter_, gap_costs_};
lane_walker_wide_t wide {substituter_, gap_costs_};
scoring_t fallback {substituter_, gap_costs_};
if (status_t status = score_scratch_.try_resize(
cross_product_candidate_lanes_scratch_(narrow, wide, fallback, queries, candidates, specs));
status != status_t::success_k)
return status;
return cross_product_candidate_lanes_range_(
narrow, wide, fallback, queries, candidates, results, cross_similarities_t::all_pairs_k, 0,
cross_live_cells_count_(queries.size(), candidates.size(), cross_similarities_t::all_pairs_k),
scratch_space_t(score_scratch_), specs);
}
template <typename queries_type_, typename candidates_type_, typename value_type_, typename executor_type_>
SZ_NOIPA status_t operator()(queries_type_ const &queries, candidates_type_ const &candidates,
strided_rows<value_type_> results, executor_type_ &&executor,
cpu_specs_t const &specs = {}) noexcept {
lane_walker_narrow_t narrow {substituter_, gap_costs_};
lane_walker_wide_t wide {substituter_, gap_costs_};
scoring_t fallback {substituter_, gap_costs_};
return cross_product_candidate_lanes_parallel_(narrow, wide, fallback, queries, candidates, results,
cross_similarities_t::all_pairs_k, score_scratch_,
std::forward<executor_type_>(executor), specs);
}
template <typename sequences_type_, typename value_type_>
SZ_NOIPA status_t operator()(sequences_type_ const &sequences, strided_rows<value_type_> results,
cpu_specs_t const &specs = {}) noexcept {
lane_walker_narrow_t narrow {substituter_, gap_costs_};
lane_walker_wide_t wide {substituter_, gap_costs_};
scoring_t fallback {substituter_, gap_costs_};
if (status_t status = score_scratch_.try_resize(
cross_product_candidate_lanes_scratch_(narrow, wide, fallback, sequences, sequences, specs));
status != status_t::success_k)
return status;
return cross_product_candidate_lanes_range_(
narrow, wide, fallback, sequences, sequences, results, cross_similarities_t::symmetric_k, 0,
cross_live_cells_count_(sequences.size(), sequences.size(), cross_similarities_t::symmetric_k),
scratch_space_t(score_scratch_), specs);
}
template <typename sequences_type_, typename value_type_, typename executor_type_>
SZ_NOIPA status_t operator()(sequences_type_ const &sequences, strided_rows<value_type_> results,
executor_type_ &&executor, cpu_specs_t const &specs = {}) noexcept {
lane_walker_narrow_t narrow {substituter_, gap_costs_};
lane_walker_wide_t wide {substituter_, gap_costs_};
scoring_t fallback {substituter_, gap_costs_};
return cross_product_candidate_lanes_parallel_(narrow, wide, fallback, sequences, sequences, results,
cross_similarities_t::symmetric_k, score_scratch_,
std::forward<executor_type_>(executor), specs);
}
#pragma endregion Public Cross Product Overloads
};
#pragma endregion Inter Sequence Cross Product Engines
#if defined(__clang__)
#pragma clang attribute pop
#elif defined(__GNUC__)
#pragma GCC pop_options
#endif
#endif #pragma endregion
} }
#endif