mdbx: добавление eq_fast() для сравнений на (не)равенство.

Цель в том, чтобы уменьшить кол-во условных и безусловных переходов при
сравнениях равно/неравно, в том числе избегать вызовов задаваемых
кастомных компаратаров и memcmp() для коротких ключей/значений.
This commit is contained in:
Леонид Юрьев (Leonid Yuriev) 2022-12-30 01:51:08 +03:00
parent 2322138a8e
commit f53dc70038

View File

@ -15236,12 +15236,31 @@ __hot static int cmp_lenfast(const MDBX_val *a, const MDBX_val *b) {
: memcmp(a->iov_base, b->iov_base, a->iov_len); : memcmp(a->iov_base, b->iov_base, a->iov_len);
} }
static bool unsure_equal(MDBX_cmp_func cmp, const MDBX_val *a, __hot static bool eq_fast_slowpath(const uint8_t *a, const uint8_t *b,
const MDBX_val *b) { size_t l) {
/* checking for the use of a known good comparator if (likely(l > 3)) {
* or/otherwise for a full byte-to-byte match */ if (MDBX_UNALIGNED_OK >= 4 && likely(l < 9))
return cmp == cmp_lenfast || cmp == cmp_lexical || cmp == cmp_reverse || return ((unaligned_peek_u32(1, a) - unaligned_peek_u32(1, b)) |
cmp == cmp_int_unaligned || cmp_lenfast(a, b) == 0; (unaligned_peek_u32(1, a + l - 4) -
unaligned_peek_u32(1, b + l - 4))) == 0;
if (MDBX_UNALIGNED_OK >= 8 && sizeof(size_t) > 7 && likely(l < 17))
return ((unaligned_peek_u64(1, a) - unaligned_peek_u64(1, b)) |
(unaligned_peek_u64(1, a + l - 8) -
unaligned_peek_u64(1, b + l - 8))) == 0;
return memcmp(a, b, l) == 0;
}
if (likely(l)) {
STATIC_ASSERT(sizeof(int) > 2);
const unsigned a3 = a[0] << 16 | a[l >> 1] << 8 | a[l - 1];
const unsigned b3 = b[0] << 16 | b[l >> 1] << 8 | b[l - 1];
return a3 == b3;
}
return true;
}
static __always_inline bool eq_fast(const MDBX_val *a, const MDBX_val *b) {
return unlikely(a->iov_len == b->iov_len) &&
eq_fast_slowpath(a->iov_base, b->iov_base, a->iov_len);
} }
/* Search for key within a page, using binary search. /* Search for key within a page, using binary search.
@ -17099,25 +17118,21 @@ static __hot int cursor_put_nochecklen(MDBX_cursor *mc, const MDBX_val *key,
flags -= MDBX_ALLDUPS; flags -= MDBX_ALLDUPS;
rc = MDBX_NOTFOUND; rc = MDBX_NOTFOUND;
exact = false; exact = false;
} else /* checking for early exit without dirtying pages */ } else if (!(flags & (MDBX_RESERVE | MDBX_MULTIPLE))) {
if (!(flags & (MDBX_RESERVE | MDBX_MULTIPLE)) && /* checking for early exit without dirtying pages */
unlikely(mc->mc_dbx->md_dcmp(data, &olddata) == 0)) { if (unlikely(eq_fast(data, &olddata))) {
if (!mc->mc_xcursor) cASSERT(mc, mc->mc_dbx->md_dcmp(data, &olddata) == 0);
/* the same data, nothing to update */ if (mc->mc_xcursor) {
return MDBX_SUCCESS; if (flags & MDBX_NODUPDATA)
if (flags & MDBX_NODUPDATA) return MDBX_KEYEXIST;
return MDBX_KEYEXIST; if (flags & MDBX_APPENDDUP)
if (flags & MDBX_APPENDDUP) return MDBX_EKEYMISMATCH;
return MDBX_EKEYMISMATCH;
if (likely(unsure_equal(mc->mc_dbx->md_dcmp, data, &olddata)))
/* data is match exactly byte-to-byte, nothing to update */
return MDBX_SUCCESS;
else {
/* The data has differences, but the user-provided comparator
* considers them equal. So continue update since called without.
* Continue to update since was called without MDBX_NODUPDATA. */
} }
/* the same data, nothing to update */
return MDBX_SUCCESS;
} }
cASSERT(mc, mc->mc_dbx->md_dcmp(data, &olddata) != 0);
}
} }
} else if (unlikely(rc != MDBX_NOTFOUND)) } else if (unlikely(rc != MDBX_NOTFOUND))
return rc; return rc;
@ -17322,28 +17337,21 @@ static __hot int cursor_put_nochecklen(MDBX_cursor *mc, const MDBX_val *key,
/* Was a single item before, must convert now */ /* Was a single item before, must convert now */
if (!(node_flags(node) & F_DUPDATA)) { if (!(node_flags(node) & F_DUPDATA)) {
/* does data match? */ /* does data match? */
const int cmp = mc->mc_dbx->md_dcmp(data, &olddata); if (flags & MDBX_APPENDDUP) {
if ((flags & MDBX_APPENDDUP) && unlikely(cmp <= 0)) const int cmp = mc->mc_dbx->md_dcmp(data, &olddata);
return MDBX_EKEYMISMATCH; cASSERT(mc, cmp != 0 || eq_fast(data, &olddata));
if (cmp == 0) { if (unlikely(cmp <= 0))
return MDBX_EKEYMISMATCH;
} else if (eq_fast(data, &olddata)) {
cASSERT(mc, mc->mc_dbx->md_dcmp(data, &olddata) == 0);
if (flags & MDBX_NODUPDATA) if (flags & MDBX_NODUPDATA)
return MDBX_KEYEXIST; return MDBX_KEYEXIST;
if (likely(unsure_equal(mc->mc_dbx->md_dcmp, data, &olddata))) { /* data is match exactly byte-to-byte, nothing to update */
/* data is match exactly byte-to-byte, nothing to update */ rc = MDBX_SUCCESS;
if (unlikely(flags & MDBX_MULTIPLE)) { if (likely((flags & MDBX_MULTIPLE) == 0))
rc = MDBX_SUCCESS; return rc;
goto continue_multiple; goto continue_multiple;
}
return MDBX_SUCCESS;
} else {
/* The data has differences, but the user-provided comparator
* considers them equal. So continue update since called without.
* Continue to update since was called without MDBX_NODUPDATA. */
}
cASSERT(mc, node_size(key, data) <= env->me_leaf_nodemax);
goto current;
} }
/* Just overwrite the current item */ /* Just overwrite the current item */