Skip to content
This repository was archived by the owner on Feb 16, 2026. It is now read-only.

Scale private oh#4364

Merged
nigoroll merged 2 commits intovarnishcache:masterfrom
nigoroll:scale_private_oh
Jul 21, 2025
Merged

Scale private oh#4364
nigoroll merged 2 commits intovarnishcache:masterfrom
nigoroll:scale_private_oh

Conversation

@nigoroll
Copy link
Copy Markdown
Member

@nigoroll nigoroll commented Jul 11, 2025

With a lot of objects on the private objhead, the mtx would become contended.

We solve this contention by using an array of private objheads and spreading objects over the array elements using a fibonacci hash.

The choice of an array of objheads rather than an array of objhead pointers is motivated by hsh_deref_objhead_unlock(), which behaves differently for private objects. An alternative to the address check would be to add a flag field to struct objhead, but that would require to grow the struct (really not a good option for such a central data structure which, in many cases, is required once per object) or reduce the size of the magic value to 16 bits.

The drawback of the chosen solution is that flexelint thinks all objheads would need to be from the array, and hence reports out of bounds access all over the place. Suppressing these for the whole source file is far from ideal, but I have not found a better option (yet). These warnings are suppressed somehow specifically (see comment inline).

For performance see #4365 (comment) : Without #4365, the benefits are not particularly visible because other locks will contend

@dridi
Copy link
Copy Markdown
Member

dridi commented Jul 11, 2025

Out of curiosity, did you try to measure contention after #4073? It removes essentially all lock operations attributed to the private_oh waiting list.

Copy link
Copy Markdown
Member

@dridi dridi left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like the idea in general, but not the complications it comes with.

I'd also like to see how #4073 affects (if at all) contention on the private_oh mutex if you can include that in your evaluation.

Comment on lines +76 to +77
#define PRIVATE_OH_EXP 7
static struct objhead private_ohs[1 << PRIVATE_OH_EXP];
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not define a constant?

#define PRIVATE_OH_EXP 7
#define PRIVATE_OH_CNT (1 << PRIVATE_OH_EXP)
static struct objhead private_ohs[PRIVATE_OH_CNT];

I understand the purpose of vcountof() but if it confuses our tooling, it's maybe not worth using.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How does it confuse our tooling?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This part of the pull request description, before you edited it to strike the second sentence out:

The drawback of the chosen solution is that flexelint thinks all objheads would need to be from the array, and hence reports out of bounds access all over the place. Suppressing these for the whole source file is far from ideal, but I have not found a better option (yet).

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The relevant part is the other bit of the description:

The choice of an array of objheads rather than an array of objhead pointers is motivated by hsh_deref_objhead_unlock(), which behaves differently for private objects.

This has nothing to do with vcountof(), what misleads flexelint is the fact that, with the static array for private objheads, the "others" are on the heap with arbitrary addresses. The reasoning of the tool is here "hey, if there is a check for an upper bound, but then an else branch, that else branch will probably be out of bounds".

I think the improved more selective silencing of flexelint is acceptable now.

Comment on lines +141 to +152
// https://probablydance.com/2018/06/16/fibonacci-hashing-the-optimization-that-the-world-forgot-or-a-better-alternative-to-integer-modulo/
static inline size_t
fib(uint64_t n, uint8_t bits)
{
const uint64_t gr = 11400714819323198485LLU;
uint64_t r;

r = n * gr;
r >>= (sizeof(gr) * 8) - bits;
assert(r < (size_t)1 << bits);
return ((size_t)r);
}
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not take the first byte of the request hash? No modulo involved, possibly one & PRIVATE_OH_MASK if you want less than 8 bits worth of private objheads.

Copy link
Copy Markdown
Member Author

@nigoroll nigoroll Jul 11, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My main driver is vcl_synth {}. As requests to the same resource have the same hash, using it would defeat the purpose of the patch and not solve the lock contention, just move it to a lock "per host+url" (actually hash, and actually "set of...").

@nigoroll
Copy link
Copy Markdown
Member Author

I like the idea in general, but not the complications it comes with.

which?

Comment thread bin/varnishd/cache/cache_hash.c Outdated
nigoroll added 2 commits July 21, 2025 15:22
With a lot of objects on the private objhead, the mtx would become contended.

We solve this contention by using an array of private objheads and spreading
objects over the array elements using a fibonacci hash.

The choice of an array of objheads rather than an array of objhead pointers is
motivated by hsh_deref_objhead_unlock(), which behaves differently for private
objects. An alternative to the address check would be to add a flag field to
struct objhead, but that would require to grow the struct (really not a good
option for such a central data structure which, in many cases, is required once
per object) or reduce the size of the magic value to 16 bits.

The drawback of the chosen solution is that flexelint thinks all objheads would
need to be from the array, and hence reports out of bounds access all over the
place. These warnings are suppressed somehow specifically (see comment inline).
@nigoroll nigoroll merged commit 445a843 into varnishcache:master Jul 21, 2025
10 checks passed
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants