https://tailscale.com/blog/netaddr-new-ip-type-for-go/
intern.go
https://github.com/go4org/intern/blob/main/intern.go
Side knowledge
Comparison operators
https://golang.org/ref/spec#Comparison_operators- Slice, map, and function values are not comparable.
- Interface values are comparable. Two interface values are equal if they have identical dynamic types and equal dynamic values or if both have value nil.
- Struct values are comparable if all their fields are comparable. Two struct values are equal if their corresponding non-blank fields are equal.
- A comparison of two interface values with identical dynamic types causes a run-time panic if values of that type are not comparable. This behavior applies not only to direct interface value comparisons but also when comparing arrays of interface values or structs with interface-valued fields.
For go4org/intern package take the fact that interface is comparable.
build declaration
//go:nocheckptr // func should not be instrumented by checkptr
Code dig
Types:
type Value struct {
_ [0]func() // prevent people from accidentally using value type as comparable
cmpVal interface{}
resurrected bool
}
// not exposed, caller would never use 'key' but value only, i.e internal cache is encapsulated.
type key struct {
s string
cmpVal interface{}
// isString reports whether key contains a string.
// Without it, the zero value of key is ambiguous.
isString bool
}
Data structure:
var (
// mu guards valMap, a weakref map of *Value by underlying value.
// It also guards the resurrected field of all *Values.
mu sync.Mutex
valMap = map[key]uintptr{} // to uintptr(*Value)
valSafe = map[key]*Value{} // non-nil in safe+leaky mode, i.e map size grow is unbounded.
)
main logic:
//go:nocheckptr
func get(k key) *Value {
mu.Lock()
defer mu.Unlock()
var v *Value
if valSafe != nil {
v = valSafe[k]
} else if addr, ok := valMap[k]; ok {
v = (*Value)((unsafe.Pointer)(addr)) // addr is uintptr, the address it holds will always be valid
v.resurrected = true
}
if v != nil {
return v
}
v = k.Value()
if valSafe != nil {
valSafe[k] = v
} else {
// SetFinalizer before uintptr conversion (theoretical concern;
// see https://github.com/go4org/intern/issues/13)
runtime.SetFinalizer(v, finalize)
valMap[k] = uintptr(unsafe.Pointer(v))
}
return v
}
func finalize(v *Value) {
mu.Lock()
defer mu.Unlock()
if v.resurrected {
// We lost the race. Somebody resurrected it while we
// were about to finalize it. Try again next round.
v.resurrected = false
runtime.SetFinalizer(v, finalize)
return
}
delete(valMap, keyFor(v.cmpVal))
}
Fact:
runtime.SetFinalizer will always make the pointer resource referenced again, until next time GC runs which has the pointer resource's SetFinalizer to nil can it be truly garbage collected.- currently, if a struct size is larger than 16 bytes, for assigning value with that struct to a passing in pointer to struct would result in allot zero size struct first than memcpy to the pointer to struct.
- Use struct size less equal to 16 bytes compiler is able to optimize to store the local created struct direct into passed in pointer to struct's heap memory.
type T struct {
a, b, c, d int
}
func f(x *T) {
t := T{}
*x = t
}
type U struct {
a, b, c, d, e int
}
func g(x *U) {
u := U{}
*x = u
}
f is compiled optimally, to:
XORPS X0, X0
MOVQ "".x+8(SP), AX
MOVUPS X0, (AX)
MOVUPS X0, 16(AX)
RET
g is quite a bit worse:
MOVQ BP, 40(SP)
LEAQ 40(SP), BP
MOVQ $0, "".u(SP)
XORPS X0, X0
MOVUPS X0, "".u+8(SP)
MOVUPS X0, "".u+24(SP)
MOVQ "".u(SP), AX
MOVQ "".x+56(SP), CX
MOVQ AX, (CX)
LEAQ 8(CX), DI
LEAQ "".u+8(SP), SI
DUFFCOPY $868
MOVQ 40(SP), BP
ADDQ $48, SP
RET
No comments:
Post a Comment
Note: Only a member of this blog may post a comment.