my personal go note
Context is context. you can pass time, value etc
create empty root context
ctx := context.Background()
create context that return timeout channel
ctx, cancel := context.WithTimeout(rootCtx, 2*time.Second)
// always defer cancel to relase memory
defer cancel()
func () {
select {
case <- time.After(3*time.Second):
fmt.Println("done")
case <- ctx.Done():
log.Println(ctx.Error())
}
}()
create context with value
// create new key type to avoid collision
type myKey
const myid = mykey(10) // 10 is rand num
ctx := context.WithValue(ctx, myid, value)
// accesing value
value, ok := ctx.Value(myid).(string) // cast vlue to type
ctx := context.Background()
ctx, cancel := context.WithTimeout(ctx, 5*time.Second)
defer cancel()
req, err := http.NewRequest(http.MethodGet, "http://localhost:8080", nil)
if err != nil {
log.Fatal(err)
}
req = req.WithContext(ctx)
res, err := http.DefaultClient.Do(req)
if err != nil {
log.Fatal(err)
}
func TestSum(t *testing.T) {
tt := []struct {
name string
value []float64
res float64
}{
{name: "basic sum", value: []float64{1, 2, 3, 4, 5}, res: 15},
{name: "with negative", value: []float64{1, -5, 8, -10}, res: -6},
{name: "with nil", value: nil, res: 0},
}
for _, tc := range tt {
t.Run(tc.name, func(t *testing.T) {
res, err := sum.Sum(tc.value...)
if err != nil {
// fatal will stop execution
t.Fatal("error while running Sum: ", err)
}
if res != tc.res {
// error do not stop the execution
t.Errorf("expected %v; got %v", tc.res, res)
}
})
}
}
example test create example and test. 2 bird with one stone
example_test.go
func ExampleSum() {
sum, err := Sum(1, 2, 3, 4, 5)
fmt.Println("sum of one to five is", sum)
// Output: sum of one to five is 15
// note: line 3 and 4 is neccessary. output must be exactly same as fmt.Println
}
func TestHttp() {
srv := httptest.NewServer(router())
defer srv.Close()
req, err := http.NewRequest(http.MethodGet, srv.URL+"/?id=10", nil)
if err != nil {
t.Fatal("error while creating request: ", err)
}
rec := httptest.NewRecorder()
handler(rec, req)
res := rec.Result()
if res.StatusCode != http.StatusOK {
t.Errorf("expected Status OK; got %v", res.Status)
}
defer res.Body.Close()
b, err := ioutil.ReadAll(res.Body)
if err != nil {
t.Fatalf("could not read body: %v", err)
}
}
// Assume T is an arbitrary type and Tkey is
// a type supporting comparison (== and !=).
*T // a pointer type
[5]T // an array type
[]T // a slice type
map[Tkey]T // a map type
// a struct type
struct {
name string
age int
}
// a function type
func(int) (bool, string)
// an interface type
interface {
Method0(string) int
Method1() (int, bool)
}
// some channel types
chan T
chan<- T
<-chan T
examples
// The following new defined and source types
// are all basic types.
type (
MyInt int
Age int
Text string
)
// The following new defined and source types are
// all composite types.
type IntPtr *int
type Book struct{author, title string; pages int}
type Convert func(in0 int, in1 bool)(out0 int, out1 string)
type StringArray [5]string
type StringSlice []string
func f() {
// The names of the three defined types
// can be only used within the function.
type PersonAge map[string]int
type MessageQueue chan string
type Reader interface{Read([]byte) int}
}
type MyType []int
type AnotherType MyType
// AnotherType has same memory structure with MyType but its different type.
// MyType != AnotherType
type MyType [2]int
type AnotherType = MyType
// now AnotherType is exactly the same as MyType. AnotherType is alias of MyType. it can even use MyType method
add this 2 line in the beginning function
pprof.StartCPUProfile(os.Stdout)
defer pprof.StopCPUProfile()
then ./program > cpu.pprof
to open: go tool pprof cpu.pprof
basic command
# list of most cpu usage
(pprof) top
# cumulative
(pprof) top -cum
# detail
(pprof) list main.main
# main.main is function name
# note: goo deeper using function name to get much more info
add this 2 line in the beginning function
pprof.StartCPUProfile(os.Stdout)
defer pprof.StopCPUProfile()
then ./program > prog.trace
open trace: go tool trace prog.trace
# benchmem also benchmark memory
$ go test -bench=. -benchmem
create pool
myPool := sync.Pool{
New: func () interface {} {
return <your pool type>
}
}
get pool
// doing this, you get ready to use object
data, ok := myPool.Get().(*<pointer your type cast>)
// return already used object to pool
defer myPool.Put(data)
// note: when you use data, make sure to initialise it to zero value first
// ex:
// data.Name = ""
// data.Age = 0
// data.Roles = nil
// the example below is wrong
// *data = Data{}
// dont do this, you just create one instance instead using available one
run go test -bench=. -benchmem -memprofile=mem.pb.gz
then pprof -http=[host]:[port] [options] source
you may need --alloc_objects
as options
anything on the heap get managed by GC, so you may avoid much possible heap escape
GC is good, but cause latency. and in some program might cause some unpredictable behaviour.
when possible, the go compiler will allocate variable that are local to a function into thaf function stackframe
however if the compiler cannot prove that the variable is not referenced after the function returns, then the compiler must allocate the variable into garbage-collected heap to avoid dagling pointer errors
run go build -gcflags "-m"
to see optimization decision
when are values constructed on the heap?
- when the value could possibly be referenced after the function that constructed the value returns
- when the compiler determine a value is too large to fit on the stack
- when the compiler doesn't know the size of the value at compile time (slice etc)
some commonly allocated values (go to heap)
- Values shared with ponters
- variable stored in interface variables
- func literal variable
- backing data for maps, channels, slices and string
which stays on the stack?
// definitely go to heap
func main() {
b := read()
}
func read() []byte {
b := make([], 32)
return b
}
// b not escape to heap
func main() {
b:= make([]byte, 32)
read(b)
}
func read(b []byte) {
// write into slive
}