Reputation: 3839
Let's imagine you have a multi-threaded running program. In my case the program uses std::thread but this is not important I suppose. Now each thread needs to write into some global buffer, but they are guaranteed to never write to the same memory address in this buffer. Questions:
The code above is just an example of what I mean by "the threads write to the same buffer but never at the same address" (though the buffer is much bigger in my case and threads access very different part of that buffer).
int *buffer = new buffer[10];
for (int i = 0; i < 10; ++i) {
threads[k] = std::thread(threadFunc, std::ref(buffer), i);
}
void threadFunc(int *&buffer, const int &threadId)
{
buffer[threadId] = threadId;
}
Upvotes: 2
Views: 3207
Reputation: 5421
...but they are guaranteed to never write to the same memory address in this buffer
This makes it thread safe. Reads would be efficient, but writes might create cache contention on some architectures.
However, the buffer would have to be rather big, because thread-ids are normally in the hundreds. You can't use a strategy like a map without synchronization, so due to the sparseness, you'd have to use a sparse array like buffer[12]
and buffer[134]
, etc. Unless you are assigning an ID of your own to each thread, which requires state in each thread... this idea is superseded by what I have below.
The best strategy is generally to have the stack of each thread hold a pointer to the static memory location and pass this context around as an argument to the various functions within the thread that need it. This is why context objects are so useful in multi-threaded programming.
Upvotes: 0
Reputation: 283773
Multithreaded access creates undefined behavior (a race condition, or worse) if two threads perform a conflicting access without synchronization.
Two accesses to the same memory location conflict. Access to separate array elements are safe.
In the Standard, 1.7p3 says that
A memory location is either an object of scalar type or a maximal sequence of adjacent bit-fields all having non-zero width. [ Note: Various features of the language, such as references and virtual functions, might involve additional memory locations that are not accessible to programs but are managed by the implementation. — end note ] Two or more threads of execution (1.10) can update and access separate memory locations without interfering with each other.
However, it probably won't be efficient. If multiple array elements fit into a single cache line, then the threads are going to fight over ownership. This is called "false sharing", and basically negates the performance advantages of having cache in the first place. To overcome this, it may be necessary to add padding so that different array elements exist in different cache lines.
Upvotes: 4