summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar Connal de Souza <connaldesouza@google.com>2023-06-09 12:38:09 -0700
committerGravatar Copybara-Service <copybara-worker@google.com>2023-06-09 12:39:02 -0700
commit1feab4fff90f904518e66cf80971063486fbc984 (patch)
treed2a9dcdec9122ff9c62b21f3c9a9b5c1d6ff489e
parente929ede4112cc21a5173578c88317c3f4c8e71cc (diff)
Optimize Cord Refcount decrement.
Introduce kHighRefcountMask which masks off flags and the LSb of the refcount value. In the cases where this mask is used, we don't need to check the LSb because we can assume the refcount is 1 when the rest of the masked RefcountAndFlags is empty, and the LSb doesn't matter if the masked value is not empty (either it's immortal or refcount > 1). This saves an instruction and a cycle (and + cmp -> tst) https://godbolt.org/z/Kz69eqfhq PiperOrigin-RevId: 539151659 Change-Id: I2ec7d72918f052c4b0938edd746af9d5b3052c7e
-rw-r--r--absl/strings/internal/cord_internal.h22
1 files changed, 15 insertions, 7 deletions
diff --git a/absl/strings/internal/cord_internal.h b/absl/strings/internal/cord_internal.h
index 8d9836ba..20dd008c 100644
--- a/absl/strings/internal/cord_internal.h
+++ b/absl/strings/internal/cord_internal.h
@@ -157,20 +157,19 @@ class RefcountAndFlags {
// false will be visible to a thread that just observed this method returning
// false. Always returns false when the immortal bit is set.
inline bool Decrement() {
- int32_t refcount = count_.load(std::memory_order_acquire) & kRefcountMask;
- assert(refcount > 0 || refcount & kImmortalFlag);
+ int32_t refcount = count_.load(std::memory_order_acquire);
+ assert((refcount & kRefcountMask) > 0 || refcount & kImmortalFlag);
return refcount != kRefIncrement &&
(count_.fetch_sub(kRefIncrement, std::memory_order_acq_rel) &
- kRefcountMask) != kRefIncrement;
+ kHighRefcountMask) != 0;
}
// Same as Decrement but expect that refcount is greater than 1.
inline bool DecrementExpectHighRefcount() {
int32_t refcount =
- count_.fetch_sub(kRefIncrement, std::memory_order_acq_rel) &
- kRefcountMask;
- assert(refcount > 0 || refcount & kImmortalFlag);
- return refcount != kRefIncrement;
+ count_.fetch_sub(kRefIncrement, std::memory_order_acq_rel);
+ assert((refcount & kRefcountMask) > 0 || refcount & kImmortalFlag);
+ return (refcount & kHighRefcountMask) != 0;
}
// Returns the current reference count using acquire semantics.
@@ -214,6 +213,15 @@ class RefcountAndFlags {
// purposes of equality. (A refcount of 0 or 1 does not count as 0 or 1
// if the immortal bit is set.)
kRefcountMask = ~kReservedFlag,
+
+ // Bitmask to use when checking if refcount is equal to 1 and not
+ // immortal when decrementing the refcount. This masks out kRefIncrement and
+ // all flags except kImmortalFlag. If the masked RefcountAndFlags is 0, we
+ // assume the refcount is equal to 1, since we know it's not immortal and
+ // not greater than 1. If the masked RefcountAndFlags is not 0, we can
+ // assume the refcount is not equal to 1 since either a higher bit in the
+ // refcount is set, or kImmortal is set.
+ kHighRefcountMask = kRefcountMask & ~kRefIncrement,
};
std::atomic<int32_t> count_;