Understand the working principle of gorang's sync.Map according to the topological diagram

Topology diagram of sync.Map

Some key points to understand the diagram

var expunged = unsafe.Pointer(new(interface{}))
type Map struct 
type readOnly struct
type entry struct
func (m *Map) Load(key interface{}) (value interface{}, ok bool)
func (m *Map) Store(key, value interface{})
func (m *Map) Delete(key interface{})
func (m *Map) Range(f func(key, value interface{}) bool)

Here, we should focus on the numerical states of readonly. Ameded, Map.misses and entry.p. in the topological diagram, many points are used to judge the trend
Next, the code and notes of the structure are listed in detail for the convenience of reading and understanding the topology map

Main structure and comments of sync.Map

type Map struct {
    //Mutex, used to lock dirty map
    mu Mutex    
    
    //Read map first and support atomic operation. If there is readOnly in the annotation, it doesn't mean read is read-only, but its structure. Read actually has a write operation
    read atomic.Value 
    
    // dirty is the latest map, which allows reading and writing
    dirty map[interface{}]*entry 
    
    // It mainly records the number of times the read map and the dirty map cannot be read by the read. When the misses is equal to the length of the dirty, the dirty map will be copied to the read
    misses int 
}

// readOnly is mainly used for storage, and elements are stored in Map.read through atomic operation.
type readOnly struct {
    // map of read, used to store all read data
    m       map[interface{}]*entry
    
    // If the data is in dirty but not in read, the value is true as the modification ID
    amended bool 
}

// entry is the specific map value of Map.dirty
type entry struct {
    // Nil: indicates that the element in read map can be set to nil by calling Delete()
    // Expunged: it also means that the key is deleted, but the key is only in read and not in dirty. This happens when you copy read to dirty. That is to say, in the process of copying, nil will be marked expunged first, and then it will not be copied to dirty
    //  Other: indicates that there is real data
    p unsafe.Pointer // *interface{}
}

The principle of sync.Map is very simple. The strategy of space for time is used. Through two redundant data structures (read and dirty), the effect of lock on performance is realized.
Two maps are introduced to separate reading and writing from each other. read map provides concurrent reading and atomic writing of existing elements, while dirty map is responsible for reading and writing.
In this way, the read map can be read concurrently without locking. When no value is read in the read map, the read map can be locked for subsequent reading, and the number of misses can be accumulated.
When the number of misses is greater than or equal to the length of the dirty map, the dirty map is raised to read map.
From the definition of the structure, we can see that although two map s are introduced, the underlying data stores pointers, pointing to the same value.

Reference documents:
sync.Map source code analysis you have to know

Tags: Go

Posted on Fri, 08 Nov 2019 13:44:20 -0500 by zonkd