False sharing occurs when multiple processors or cores modify variables that reside on the same cache line, causing unnecessary cache coherence traffic. This can lead to significant performance degradation in concurrent applications. Even though the variables are logically independent, the hardware cache treats them as a single entity due to their physical proximity, resulting in performance penalties.
A cache line is the smallest unit of data that can be transferred between the main memory and the CPU cache. On modern processors, a cache line is typically 64 bytes. When a variable is modified, the entire cache line containing that variable may need to be synchronized across multiple CPU cores, even if other variables within the same cache line are not related to the current operation.
Consider the following example where false sharing might occur:
gopackage main
import (
"sync"
"sync/atomic"
"time"
)
type Counter struct {
x int64
y int64
}
func main() {
counter := &Counter{}
var wg sync.WaitGroup
wg.Add(2)
go func() {
defer wg.Done()
for i := 0; i < 1e6; i++ {
atomic.AddInt64(&counter.x, 1)
}
}()
go func() {
defer wg.Done()
for i := 0; i < 1e6; i++ {
atomic.AddInt64(&counter.y, 1)
}
}()
wg.Wait()
}
In this example, counter.x
and counter.y
are likely to be placed on the same cache line, causing false sharing when both goroutines update these variables concurrently.
To avoid false sharing, you can use padding to ensure that variables likely to be accessed concurrently are placed on separate cache lines. This can be done by adding padding fields between the variables.
gopackage main
import (
"sync"
"sync/atomic"
)
type PaddedCounter struct {
x int64
_ [7]int64 // Padding to prevent false sharing
y int64
}
func main() {
counter := &PaddedCounter{}
var wg sync.WaitGroup
wg.Add(2)
go func() {
defer wg.Done()
for i := 0; i < 1e6; i++ {
atomic.AddInt64(&counter.x, 1)
}
}()
go func() {
defer wg.Done()
for i := 0; i < 1e6; i++ {
atomic.AddInt64(&counter.y, 1)
}
}()
wg.Wait()
}
In this example, the padding ensures that counter.x
and counter.y
are placed on separate cache lines, eliminating false sharing.
unsafe
package or struct tags, although it requires careful consideration and understanding of the hardware architecture.pprof
to identify hotspots in your code.False sharing can significantly impact the performance of concurrent applications by causing unnecessary cache coherence traffic. By understanding the concept of cache lines and strategically using padding, you can avoid false sharing and improve the performance of your Go applications. Always remember to profile your changes and measure their impact to ensure that your optimizations are effective.