Showing posts with label golang_trick. Show all posts
Showing posts with label golang_trick. Show all posts

Mar 25, 2021

[Go] memclr for array and slice (not pointer to array)

Reference:
https://github.com/golang/go/issues/5373

Quote:

There should be a fast way to zero memory. When implementing object pools (reusing []byte or []int), for safety the application's pool allocator needs to zero memory before giving it back out to callers.  Currently Go can't do it very quickly.

Ideally, yes.

Rob was talking about this, so I filed a bug.

I'd also want:

for i := range b {

   b[i] = MyStruct{}

}

... to be recognized.


memclr Optimization:

for i := range a {
	a[i] = t0
}

s := a[:]
for i := range s {
	s[i] = t0
}

[Go][ticket][trick] make 64-bit fields 64-bit aligned on 32-bit systems

Ticket:
cmd/compile: make 64-bit fields 64-bit aligned on 32-bit systems
https://github.com/golang/go/issues/599

Reference:


The idea below is to avoid any padding, thus using 15 bytes, which if using 14 bytes will be used by later 2 bytes short type.
package mylib

import (
	"unsafe"
	"sync/atomic"
)

type Counter struct {
	x [15]byte // instead of "x uint64"
}

func (c *Counter) xAddr() *uint64 {
	// The return must be 8-byte aligned.
	return (*uint64)(unsafe.Pointer(
		uintptr(unsafe.Pointer(&c.x)) + 8 -
		uintptr(unsafe.Pointer(&c.x))%8))
}

func (c *Counter) Add(delta uint64) {
	p := c.xAddr()
	atomic.AddUint64(p, delta)
}

func (c *Counter) Value() uint64 {
	return atomic.LoadUint64(c.xAddr())
}

[Go]noCopy trick

 Reference:
https://github.com/golang/go/blob/374b1904750931ed09d342e3c4c6e01fdb2802aa/src/sync/cond.go#L94
https://github.com/golang/go/issues/8005#issuecomment-190753527

quote RSC:

There have been a number of OK-but-not-obviously-right proposals for saying "you can't copy this". My view of the situation is:

Go doesn't support this today, and in fact it used to but we took that out.

The original motivation here was sync.Mutex, which is handled by the current cmd/vet copylock check (which looks for a Lock method), and sync.Cond, which isn't handled but could easily be added to vet in some way.

The most common way to create types that cannot be copied is to embed a sync.Mutex, and those types are already handled by the cmd/vet check.

Instead of building more generality into vet as a kind of back-door language change, let's leave things alone for now - without any attempt to expand the generality of the copylock check - and wait to see if a better idea or more compelling evidence comes along. We should probably also make sure vet understands that sync.Cond cannot be copied, if it does not already. I opened a separate issue for that: #14582.

Note that code that absolutely must opt in to the vet check can already do so. A package can define:

type noCopy struct{}
func (*noCopy) Lock() {}

and then put a noCopy noCopy into any struct that must be flagged by vet.

--end quote--

Thus;

// noCopy may be embedded into structs which must not be copied
// after the first use.
//
// See https://golang.org/issues/8005#issuecomment-190753527
// for details.
type noCopy struct{}

// Lock is a no-op used by -copylocks checker from `go vet`.
func (*noCopy) Lock()   {}
func (*noCopy) Unlock() {}
Which whenever a struct embedded noCopy can only pass in interface as pointer type.


package main


type noCopy struct{}

func (*noCopy) Lock() {}
func (*noCopy) UnLock() {}


type I1 interface{
    Lock()
    UnLock()
}

type Fun struct{
    noCopy
}

func main() {

    f1 := Fun{}
    f2 := f1
    _ = f2

    var i1 I1 = &f1

    _ = i1
}

Mar 11, 2021

[Go][code dig] resource resurrection of using runtime.SetFinalizer

Reference:
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.

//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

Apr 23, 2020

[Go] posix thread TLS(slow) call by Go

Thread Local Storage(TLS)  is slow. (TLPI Ch.31.3.1, Ch.31.4)

Discussion thread:
https://groups.google.com/forum/?utm_medium=email&utm_source=footer#!msg/golang-nuts/tGamryo50BY/gKxPGwBdAwAJ


Steps:

  1. Using runtime.LockOSThread to lock this go-routine to the underlying P
  2. Call cgo/c SO.
  3. Use channel to pass down messages, other go-routine might be scheduled out of this P(most likely won't if listen to same channel)

Apr 2, 2020

[Go] use sync.Pool with sense

https://github.com/golang/go/issues/23199

tl;dr
It's caller's responsibility to put back normalized size of instance back into
sync.Pool

Intermingling small and large size of instance put into the sync.Pool will
eventually getting all the instance inside the pool with large size.
(Read Reference 1)

thus either create a bunch of sync.Pool bucketize the items by size (aka. slab memory allocation) or only put back certain size of instance back to the pool.

Same idea as goroutin instance recycling.
Go runtime will not recycle any goroutin size > 2k.


Reference:
https://github.com/golang/go/issues/23199
http://vsdmars.blogspot.com/2019/01/split-stack-reading-notes-and-references.html

Reason:
the buffer inside the sync.Pool will remain LARGE and never shrink even serving small objects
code:
pool := sync.Pool{New: func() interface{} { return new(bytes.Buffer) }}

processRequest := func(size int) {
	b := pool.Get().(*bytes.Buffer)
	time.Sleep(500 * time.Millisecond) // Simulate processing time
	b.Grow(size)
	pool.Put(b)
	time.Sleep(1 * time.Millisecond) // Simulate idle time
}

// Simulate a set of initial large writes.
for i := 0; i < 10; i++ {
	go func() {
		processRequest(1 << 28) // 256MiB
	}()
}

time.Sleep(time.Second) // Let the initial set finish

// Simulate an un-ending series of small writes.
for i := 0; i < 10; i++ {
	go func() {
		for {
			processRequest(1 << 10) // 1KiB
		}
	}()
}

// Continually run a GC and track the allocated bytes.
var stats runtime.MemStats
for i := 0; ; i++ {
	runtime.ReadMemStats(&stats)
	fmt.Printf("Cycle %d: %dB\n", i, stats.Alloc)
	time.Sleep(time.Second)
	runtime.GC()
}

Nov 11, 2019

[Go] profiling trick

Tip: Use the built-in go test flags if you need to profile your benchmarks:

-test.cpuprofile
-test.memprofile
-test.mutexprofile
-test.blockprofile

$ go test -bench=BenchmarkX -test.cpuprofile profile.out
$ go tool pprof profile.out

[Go] bit trick

package main
  
  func main() {
    var i = 128 // int
    m, n := byte(i), int8(i)
    println(m, -m, m == -m) // 128 128 true
    println(n, -n, n == -n) // -128 -128 true
  }

Sep 2, 2019

[Go][tricks] API compatibility

Reference:
GopherCon 2019: Jonathan Amsterdam - Detecting Incompatible API Changes
https://www.youtube.com/watch?v=JhdL5AkH-AQ
Slides:
https://about.sourcegraph.com/go/gophercon-2019-detecting-incompatible-api-changes
Spec:
https://go.googlesource.com/exp/+/refs/heads/master/apidiff/README.md
Tools binary:
$ go get golang.org/x/exp/cmd/apidiff  # will be replaced by gorelease
Golang /x/tools/go/packages
https://godoc.org/golang.org/x/tools/go/packages
Golang types package
https://golang.org/pkg/go/types/
Golang constant package
https://golang.org/pkg/go/constant/
Golang gcexportdata package
https://godoc.org/golang.org/x/tools/go/gcexportdata


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.

Go v1.15
Use an unexported, zero-width, non-comparable field
(Function values, Slice values and Map values are not comparable
https://golang.org/ref/spec#Comparison_operators ), to
prevent clients from comparing a struct and shrink binary size.
(Using 0 size of array instead of pure Function/Slice/Map which holds a size
of pointer points to memory):
type Point struct {
 _ [0] func()
 X, Y int
}