mdbx: using e2k-frendly/cmov/branch-less bsearch.

https://gitflic.ru/project/erthink/bsearch-try
This commit is contained in:
Леонид Юрьев (Leonid Yuriev) 2022-08-09 00:24:44 +03:00
parent 3de759a7be
commit 98c53555ab

View File

@ -2277,52 +2277,73 @@ static int lcklist_detach_locked(MDBX_env *env) {
/*------------------------------------------------------------------------------ /*------------------------------------------------------------------------------
* LY: Binary search */ * LY: Binary search */
#if defined(__clang__) && __clang_major__ > 4 && defined(__ia32__)
#define WORKAROUND_FOR_CLANG_OPTIMIZER_BUG(size, flag) \
do \
__asm __volatile("" \
: "+r"(size) \
: "r" /* the `b` constraint is more suitable here, but \
cause CLANG to allocate and push/pop an one more \
register, so using the `r` which avoids this. */ \
(flag)); \
while (0)
#else
#define WORKAROUND_FOR_CLANG_OPTIMIZER_BUG(size, flag) \
do { \
/* nope for non-clang or non-x86 */; \
} while (0)
#endif /* Workaround for CLANG */
#define BINARY_SEARCH_STEP(TYPE_LIST, CMP, it, size, key) \
do { \
} while (0)
#define SEARCH_IMPL(NAME, TYPE_LIST, TYPE_ARG, CMP) \ #define SEARCH_IMPL(NAME, TYPE_LIST, TYPE_ARG, CMP) \
static __always_inline const TYPE_LIST *NAME( \ static __always_inline const TYPE_LIST *NAME( \
const TYPE_LIST *first, unsigned length, const TYPE_ARG item) { \ const TYPE_LIST *it, unsigned length, const TYPE_ARG item) { \
const TYPE_LIST *const begin = first, *const end = begin + length; \ const TYPE_LIST *const begin = it, *const end = begin + length; \
\ \
while (length > 3) { \ if (MDBX_HAVE_CMOV) \
const unsigned whole = length; \ do { \
length >>= 1; \ /* Адаптивно-упрощенный шаг двоичного поиска: \
const TYPE_LIST *const middle = first + length; \ * - без переходов при наличии cmov или аналога; \
const unsigned left = whole - length - 1; \ * - допускает лишние итерации; \
const bool cmp = expect_with_probability(CMP(*middle, item), 0, .5); \ * - но ищет пока size > 2, что требует дозавершения поиска \
length = cmp ? left : length; \ * среди остающихся 0-1-2 элементов. */ \
first = cmp ? middle + 1 : first; \ const TYPE_LIST *const middle = it + (length >> 1); \
} \ length = (length + 1) >> 1; \
\ const bool flag = expect_with_probability(CMP(*middle, item), 0, .5); \
switch (length) { \ WORKAROUND_FOR_CLANG_OPTIMIZER_BUG(length, flag); \
case 3: \ it = flag ? middle : it; \
if (expect_with_probability(!CMP(*first, item), 0, .5)) \ } while (length > 2); \
break; \ else \
++first; \ while (length > 2) { \
__fallthrough /* fall through */; \ /* Вариант с использованием условного перехода. Основное отличие в \
case 2: \ * том, что при "не равно" (true от компаратора) переход делается на 1 \
if (expect_with_probability(!CMP(*first, item), 0, .5)) \ * ближе к концу массива. Алгоритмически это верно и обеспечивает \
break; \ * чуть-чуть более быструю сходимость, но зато требует больше \
++first; \ * вычислений при true от компаратора. Также ВАЖНО(!) не допускается \
__fallthrough /* fall through */; \ * спекулятивное выполнение при size == 0. */ \
case 1: \ const TYPE_LIST *const middle = it + (length >> 1); \
if (expect_with_probability(!CMP(*first, item), 0, .5)) \ length = (length + 1) >> 1; \
break; \ const bool flag = expect_with_probability(CMP(*middle, item), 0, .5); \
++first; \ if (flag) { \
__fallthrough /* fall through */; \ it = middle + 1; \
case 0: \ length -= 1; \
break; \ } \
default: \ } \
__unreachable(); \ it += length > 1 && expect_with_probability(CMP(*it, item), 0, .5); \
} \ it += length > 0 && expect_with_probability(CMP(*it, item), 0, .5); \
\ \
if (mdbx_audit_enabled()) { \ if (mdbx_audit_enabled()) { \
for (const TYPE_LIST *scan = begin; scan < first; ++scan) \ for (const TYPE_LIST *scan = begin; scan < it; ++scan) \
assert(CMP(*scan, item)); \ assert(CMP(*scan, item)); \
for (const TYPE_LIST *scan = first; scan < end; ++scan) \ for (const TYPE_LIST *scan = it; scan < end; ++scan) \
assert(!CMP(*scan, item)); \ assert(!CMP(*scan, item)); \
(void)begin, (void)end; \ (void)begin, (void)end; \
} \ } \
\ \
return first; \ return it; \
} }
/*----------------------------------------------------------------------------*/ /*----------------------------------------------------------------------------*/