Introduction

Those are my notes on ArdanLabs ultimate Bundle course. You may find them help full because Bill know what his doing. And remember those are notes. Bill talk more about details and examples and more information that I know but you may not know. Also I write them based on my understanding including the drawings and diagrams.


Variables

  • Type is life

  • Type tell us what this memory represent , let's say we have this byte [00001010] if the type is int then it's represent 10 , but if the type is color then our chart represent 10 as RED so it's RED.

  • we can be precise with our type like int16 , float64

  • If the type wasn't obvious the code review will stop

  • we need to be empathetic (Arabic : motaatef ) with the hardware , we use type based on hardware if it's an 32 or 64 bit and we can let the compiler choose by typing int . But sometimes we need to be explicit with the architecture so we need an int of certain width , that's why we have options.

  • We talked about integrity as highest priority , GO has Zero value , means when memory is allocated it need to be initialized he will set the memory to 0.

  • var denote a 0 value , that's tell us that we need to initialize our value to 0 (ex : var a int)

  • there is many ways to represent strings , GO have unique implementation do it by divide memory into 2 words (word : generic (Arabic : takhassos ) allocation that used as fixed size depend on the architect design - exemple in 32-bit machine a word is 32-bit) , the first is a pointer allocated to nil and second word is int that represent the number of bytes for string.

  • you can use a := 0 , it's okay . But it's better to use var for readability.
  • s := "Hello"

image-20200821084458816

the pointer will take address of first character and second word will take the number of characters.

  • GO have converging over casting . casting can cost us us lot of bugs . It tell the compiler "Hey I know that I allocate only 1 byte int but let's pretend that's 4 byte int" , GO have unsafe package to do this.

    GO will take the old allocation and move it to a new allocation which is bigger.

    image-20200821085648916

Struct Types

  • image-20200821090147353

  type example struct {
      flag  bool
      counter   int16
      pi        float32
  }
  1. bool is 1 byte

  2. int16 is 2 bytes

  3. float32 is 4

this is 1+2+4 = 7 bytes but this is not the case it's 8. Why because there is something called alignment. image-20200821094254933

  • every word equal to one operation so if we our struct need more space it will take a new word and it may split the variable to 2 words .
  • Every word = one operation , so if we don't have alignment it will be 2 operations.

image-20200821095035060

  • Alignment take extra empty space to make it happen in 1 operation.

  • To reduce size of padding we can arrange the types in the struct from biggest to smallest . But should not doing that unless we have a problem.

  type example struct {
      counter   int64
      pi        float32
      flag      bool
  }
  • We don't need to use var for struct unless we are going to return it inside a function.

  • if you have 2 identical structs (same memory model) but different names , in GO struct1 and struct2 are not the same. so if we take 2 variables one is with type struct1 and other struct2 we can say a = b . Because GO see them as 2 different models. In other languages can perform this (called implicit conversion ) but not in GO.

    Bill talk more and more about this and different example and how to fix it in the video.

Pointers - Pass by values

image-20200821122745931

P : stands for logical processor m : Machine (real life OS thread) , it will be scheduled by real operation system. HW: real hardware "playground (official on line go compiler) have 1 , on local machine it depend on CPU" G : GO routine is very similar to M , but G is application layer thread , M is OS thread.

  • GO routine (G) get a stack(memory where last in first out) of 2K on windows and 1M on Linux , Linux is better because is allocate stack based on OS page file size and if it's need memory will grow.

  • take advantages on hardware , because all the new applications are running on the cloud where every saving in resources matter because it will cost you.

  • We don't care about reading as much of modifying or writing to memory because here where it cause bugs.

  • Address (is where the box located) , value (is what inside the box) , Data is both.

  • When a program start the GO routine start from main and if a function is called inside main the go routine will jump into other area in the memory where its isolated from main memory.

    image-20200821125958175

  • passing as value it's mean you are passing a value from box 1 to box 2 , and this copy have a different allocation in memory than box 1. Every change you do on the copy in the function(box 2) doesn't affect it in the main (box 1)

    Sharing Data

  • if we need to ask about address we can't just use * we need to specify the type of it because we are reading and writing into memory so we need to know the type of the memory if it's int we specify it as *int , code here

    func increment(inc *int)
  • this is not pass by reference it's still pass by value.

  • Don't think now about when to use value semantics and pointer semantics , rather focus now on understand the differences.

  • the pointer have his own address and his value is the address of other variable.

  • when you use/see a pointer you need to slow it down to see if his causing a side effects or not , because GO routine going from box to another

Escape Analysis

  • factory is a function or method that returns objects of a varying prototype or class from some method call, which is assumed to be "new". -- Wikipedia

  • stack is self cleaning so garbage collector he doesn't care about it , because it clean it self.

  • Escape Analysis is used by GO compiler it reads your code at compile time so it's determine where your value should be constructed on the stack or on the heap.

  • Escape Analysis is about how the value is shared.

  • When the compiler see that we are sharing things up (returning address of a variable declared inside the function to main) to the stack he know that we should store it on the heap.

  • In the heap garbage collector will be involved to clean.

  • you can use as u := &user instead of every time type &u if you are passing your object to an API , but it's not good in code reviews because we need to see that &u for readability , we need to see that cause a cost and sharing data outside of that function.

  • to see Escape analysis report we use -gcflags -m=2 , gc stands for go compiler , it's gonna build your app but it will show you the analysis of what is going on.

Stack Growth

  • every function call will take a frame from the stack , In the end we will have no room left on the stack.

image-20200821155714598

  • We can't just shut the program down it will growth the stack , compiler those days use contiguous(Arabic : Motakariba) stack. It means that he will create a new stack that will cost us a new stack with size of 2*old .

  • The values start to move from old stack to the new one.

  • Image with have 50K of GO routines and every GO routine have his own stack , and we have allocations to pointers from GO routine to another one. if a stack need to growth it need to move all the pointers with it.

    that's why in GO routine can't share value with other GO routine that it is in his Own stack.

  • if we need to do an Escape Analysis we need to move the pointer to the Heap.

Garbage Collection

  • We don't care about know the full implementation of garbage collector.
  • The team should be allowed to replace the garbage collector with anything that he wants.
  • All collectors have the same job , it walk into the heap see all the variables that they are allocated but no used anymore and sweep(clear) there memory to free.
  • the pacer it's a big deal his job is to figure out when to start a collection , how long that collection is going to take. He make sure to start the collection very at the end moment he can finish before we run out of heap space.
  • when we finish garbage collecting we ending up with 2 heaps.
  • one is inUse and another one is called GAP. the second have 2*first.
  • this is internal latency because the faster the garbage collector finish his job is better.
  • garbage collector take 25% of our cpu if we have 4 GO routines his going to take 1 , if we have 8 his going to take 2. image-20200821182905723
  • if the garbage collector feel that his running out of time , he go to another GO routine and take his place for temporary time.
  • The garbage collector is here to write multi threaded software without thinking about where memory is and how it's been shared . So it's big win to have it. But nothing is free , to have that win we need to live with that latency cost , our job is how we can be sympathetic with that garbage collector to help him reduce the time of stop the world , how to reduce how long the garbage collector is gonna take per collection , how we can reduce the number of garbage collectors we need to use in large amount of work.
  • image-20200821184645714

GC (Garbage collection) , 2ms to sweep a collection.

if it's taking 300 ms + 80 ms Stop the world latency

  • making the heap bigger is not gonna help because the smaller the heap is the faster it is to check. smaller heap = less work.
  • We can reduce the number of GC and the number of variables on the heap to make reduce latency.

Constants

  • In other language constants are read only variables. But in go is the complete opposite they aren't variables at all.

  • Constants exist only on run time.

  • constants can be a type or a kind.

  • kind doesn't have an type on initializing

  const ui      = 12345 // kind : integer
  const ti int  = 12345 // type : int
  • kind can be very large and precision , type will take max of his type

  • if type go after his limits we get overflow error

  • const variable_name = n1 op n2 (for all operations kind will be kind of high priority ex : int*float = float)

  • we have in GO constant blocks

  const (
    A1 = iota // 0
      B1 = iota // 1
      C1 = iota // 2
  )

iota will always start at 0 by default and every time will increment by 1

  • you can initialize it at any number by adding number to iota example A2 = iota+1

  • time package using iota to do time shifting (Bitmasking)

  const (
      Ldate         = 1 << iota // 1: Shift 1 to the left 0. 0000 0001
      Ltime                     // 2: Shift 1 to the left 1. 0000 0010
      Lmicroseconds             // 4: Shift 1 to the left 2. 0000 0100
  )

This post is also available on DEV.