glib/gvariant: skip bitlock for g_variant_ref_sink()

In almost all cases, the variant is sunk shortly after creation and
certainly before other threads get a view of it.

This skips the bitlock for STATE_FLOATING, which is also done in
g_variant_take_ref(). We can do this because STATE_FLOATING may only
be set up-front, never after being cleared.

That allows us to do a relaxed read of the value once (which if it is
zero means no additional atomics beyond our ref count increment) as well
as a single atomic if we do in fact steal the floating reference.

Without any other GVariant performance changes, this is in the 2% range
of benchmarking a tight loop using GVariantBuilder. However, after the
rest of them are applied, the percentage is greater due to reduced
runtime overhead and lands in the 4.5% range.
This commit is contained in:
Christian Hergert 2024-09-24 18:24:55 -07:00
parent 84b6f747cb
commit 3566aeb079

View File

@ -863,19 +863,24 @@ g_variant_ref (GVariant *value)
GVariant *
g_variant_ref_sink (GVariant *value)
{
int old_state;
g_return_val_if_fail (value != NULL, NULL);
g_return_val_if_fail (!g_atomic_ref_count_compare (&value->ref_count, 0), NULL);
g_variant_lock (value);
TRACE(GLIB_VARIANT_REF_SINK(value, value->type_info, value->ref_count, value->state, value->state & STATE_FLOATING));
if (~value->state & STATE_FLOATING)
g_variant_ref (value);
else
value->state &= ~STATE_FLOATING;
old_state = value->state;
g_variant_unlock (value);
while (old_state & STATE_FLOATING)
{
int new_state = old_state & ~STATE_FLOATING;
if (g_atomic_int_compare_and_exchange_full (&value->state, old_state, new_state, &old_state))
return value;
}
g_atomic_ref_count_inc (&value->ref_count);
return value;
}