Hongli
Hongli

Reputation: 18924

Caching read-mostly values: are time lookups cheaper than atomic operations?

My multithreaded app uses a bunch of read-mostly values. These values are configuration values, and only change when the operator edits the config file and instructs the app to reload the config file without downtime. The values are accessed from multiple threads. None of the threads mutate the value. Mutation only occurs when the config file is reloaded.

Because the values can change, accessing them requires some form of synchronization. But because they change so rarely, I do not want to use mutexes:

I could go lower level and use atomic operations directly. For example, I can make the Config object immutable, with an atomic pointer to the latest version:

However, atomic operations themselves are not free either. I have a hard time finding information on what sort of overhead they exactly impose (CPU pipeline stalls? Some sort communication overhead between CPU cores? Do they limit concurrency? Not sure.) but I get the feeling that it's better to avoid them when possible.

So I got the idea of caching the config pointer for a limited amount of time, e.g. for 1 second. The cached pointer is accessed without synchronization. But this assumes that time lookups are less expensive and have less impact on concurrency than atomic pointer operations. Is this true?

So my main question is:

My secondary questions (for better understanding the problem) are:

Additional information about my environment and use case:

Upvotes: 1

Views: 56

Answers (1)

usr
usr

Reputation: 171178

I'll give a partial answer:

All low level languages have a way to cheaply and atomically load a value from memory. This does not require an interlocked access on x86. In fact, on x86 it is just a regular load instruction. All we need to do is to prevent the compiler and runtime (if any) from reordering this memory access. It's a compiler barrier.

In C# this facility is surfaced through the Volatile class. In C++ there is now atomic. I'm not sure what consistency level is required. It's probably an acquire load (which is cheap on x86). MSVC also applies these semantics to volatile variables. I do not know about other compilers. The C standard certainly does not specify these semantics for volatile but some compilers do.

This facility is so cheap that I think you can stop looking for anything else. This operation should hit a shared CPU cache line almost always (except right after a store has been made).

See Herb Sutters video series "Atomic Weapons" for more details. Frankly, he's also a more reliable source than I am.

Upvotes: 1

Related Questions