False sharing

From Wikipedia, the free encyclopedia
Jump to navigation Jump to search

In computer science, false sharing is a performance-degrading usage pattern that can arise in systems with distributed, coherent caches at the size of the smallest resource block managed by the caching mechanism. When a system participant attempts to periodically access data that is not being altered by another party, but that data shares a cache block with data that is being altered, the caching protocol may force the first participant to reload the whole cache block despite a lack of logical necessity.[1] The caching system is unaware of activity within this block and forces the first participant to bear the caching system overhead required by true shared access of a resource.

Multiprocessor CPU caches[edit]

By far the most common usage of this term is in modern multiprocessor CPU caches, where memory is cached in lines of some small power of two word size (e.g., 64 aligned, contiguous bytes). If two processors operate on independent data in the same memory address region storable in a single line, the cache coherency mechanisms in the system may force the whole line across the bus or interconnect with every data write, forcing memory stalls in addition to wasting system bandwidth. In some cases, the elimination of false sharing can result in order-of-magnitude performance improvements.[2] False sharing is an inherent artifact of automatically synchronized cache protocols and can also exist in environments such as distributed file systems or databases, but current prevalence is limited to RAM caches.

Example[edit]

#include <thread>
#include <new>
#include <atomic>

using namespace std;

constexpr bool FALSE_SHARING = true;
constexpr size_t
#if defined(__cpp_lib_hardware_interference_size)
	CACHE_LINE_SIZE = hardware_destructive_interference_size,
#else
	CACHE_LINE_SIZE = 64,
#endif
	SECOND_ALIGN = FALSE_SHARING ? sizeof(int) : CACHE_LINE_SIZE;

using atomic_type = atomic<int>;

struct shared_or_not
{
	atomic_type a alignas(CACHE_LINE_SIZE);
	atomic_type b alignas(SECOND_ALIGN);
};

int main()
{
	shared_or_not sharedOrNot;
	auto theThread = []( atomic_type *atomicValue )
	{
		for( size_t r = 100'000'000; r--; )
			++*atomicValue;
	};
	thread
		threadA( theThread, &sharedOrNot.a ),
		threadB( theThread, &sharedOrNot.b );
	threadA.join();
	threadB.join();
}

This C++20-code shows the impact of false sharing. The first member a of shared_or_not is always aligned on a cacheline beginning to prevent the data structure to straddle a cacheline so that a and b are accidentally split and there's no way to show the effect of false sharing. The second member b is directly placed after a in the same cacheline or aligned on the next cacheline depending on if FALSE_SHARING is set to true or false. If FALSE_SHARING is set to false the code usually runs multiple times faster.

Mitigation[edit]

There are ways of mitigating the effects of false sharing. For instance, false sharing in CPU caches can be prevented by reordering variables or adding padding (unused bytes) between variables. However, some of these program changes may increase the size of the objects, leading to higher memory use.[2] Compile-time data transformations can also mitigate false-sharing.[3] However, some of these transformations may not always be allowed. For instance, the C++ programming language standard draft of C++23 mandates that data members must be laid out so that later members have higher addresses.[4]

There are tools for detecting false sharing.[5][6] There are also systems that both detect and repair false sharing in executing programs. However, these systems incur some execution overhead.[7][8]

References[edit]

  1. ^ Patterson, David (2012). Computer organization and design: the hardware/software interface. Waltham, MA: Morgan Kaufmann. p. 537. ISBN 978-0-12-374750-1. OCLC 746618653.
  2. ^ a b Bolosky, William J.; Scott, Michael L. (1993-09-22). "False sharing and its effect on shared memory performance". Sedms'93: USENIX Systems on USENIX Experiences with Distributed and Multiprocessor Systems. 4. Retrieved 11 July 2021.
  3. ^ Jeremiassen, Tor E.; Eggers, Susan J. (1995). "Reducing false sharing on shared memory multiprocessors through compile time data transformations". ACM SIGPLAN Notices. Association for Computing Machinery (ACM). 30 (8): 179–188. doi:10.1145/209937.209955. ISSN 0362-1340.
  4. ^ "Working Draft, Standard for Programming Language C++ [class]". eel.is. Retrieved 2021-07-11.
  5. ^ "perf-c2c(1)". Linux manual page. 2016-09-01. Retrieved 2021-08-08.
  6. ^ Chabbi, Milind; Wen, Shasha; Liu, Xu (2018-02-10). Featherlight on-the-fly false-sharing detection. New York, NY, USA: ACM. doi:10.1145/3178487.3178499.
  7. ^ Nanavati, Mihir; Spear, Mark; Taylor, Nathan; Rajagopalan, Shriram; Meyer, Dutch T.; Aiello, William; Warfield, Andrew (2013). Whose cache line is it anyway?. New York, New York, USA: ACM Press. doi:10.1145/2465351.2465366.
  8. ^ Liu, Tongping; Berger, Emery D. (2011-10-18). "SHERIFF: precise detection and automatic mitigation of false sharing". ACM SIGPLAN Notices. Association for Computing Machinery (ACM). 46 (10): 3–18. doi:10.1145/2076021.2048070. ISSN 0362-1340.

External links[edit]