// NewFreeTree returns a new FreeTree using the data from a supplied SimpleTree. func NewFreeTree(st *SimpleTree) (*FreeTree, error) { nbNodes := st.nodes nodeChunk, err := mmm.NewMemChunk(freeNode{}, nbNodes) if err != nil { return nil, err } dataChunk, err := mmm.NewMemChunk(st.root.data, nbNodes) if err != nil { return nil, err } ft := &FreeTree{nodeChunk: nodeChunk, dataChunk: dataChunk} for _, n := range st.flattenNodes() { node := (*freeNode)(unsafe.Pointer(ft.nodeChunk.Pointer(int(n.id)))) node.id = n.id if n.left != nil { node.left = nodeChunk.Pointer(int(n.left.id)) } if n.right != nil { node.right = nodeChunk.Pointer(int(n.right.id)) } dataChunk.Write(int(n.id), n.data) if n == st.root { ft.root = node } } return ft, nil }
func main() { // create a new memory chunk that contains 3 Coordinate structures mc, err := mmm.NewMemChunk(Coordinate{}, 3) if err != nil { log.Fatal(err) } // print 3 fmt.Println(mc.NbObjects()) // write {3,9} at index 0, then print {3,9} fmt.Println(mc.Write(0, Coordinate{3, 9})) // write {17,2} at index 1, then print {17,2} fmt.Println(mc.Write(1, Coordinate{17, 2})) // write {42,42} at index 2, then print {42,42} fmt.Println(mc.Write(2, Coordinate{42, 42})) // print {17,2} fmt.Println(mc.Read(1)) // print {42,42} fmt.Println(*((*Coordinate)(unsafe.Pointer(mc.Pointer(2))))) // free memory chunk if err := mc.Delete(); err != nil { log.Fatal(err) } }
func main() { ///////////////////////////////////////////////// // A: Managed heap, 10 million pointers to int // ///////////////////////////////////////////////// fmt.Println(`Case A: what happens when we store 10 million pointers to integer on the managed heap?` + "\n") // build 10 million pointers to integer on the managed heap ints := make([]*int, 10*1e6) // init our pointers for i := range ints { j := i ints[i] = &j } // get rid of init garbage runtime.GC() debug.FreeOSMemory() for i := 0; i < 5; i++ { // randomly print one of our integers to make sure it's all working // as expected, and to prevent them from being optimized away fmt.Printf("\tvalue @ index %d: %d\n", i*1e4, *(ints[i*1e4])) // run GC now := time.Now().UnixNano() runtime.GC() fmt.Printf("\tGC time (managed heap, 10 million pointers): %d us\n", (time.Now().UnixNano()-now)/1e3) } // Results: // value @ index 0: 0 // GC time (managed heap, 10 million pointers): 329840 us // value @ index 10000: 10000 // GC time (managed heap, 10 million pointers): 325375 us // value @ index 20000: 20000 // GC time (managed heap, 10 million pointers): 323367 us // value @ index 30000: 30000 // GC time (managed heap, 10 million pointers): 327905 us // value @ index 40000: 40000 // GC time (managed heap, 10 million pointers): 326469 us fmt.Println() ////////////////////////////////////////////////////// // B: Unmanaged heap, pointers generated on-the-fly // ////////////////////////////////////////////////////// fmt.Println(`Case B: mmm doesn't store any pointer, it doesn't need to. Since the data is stored on an unmanaged heap, it cannot be collected even if there's no reference to it. This allows mmm to generate pointers only when something's actually reading or writing to the data.` + "\n") // build 10 million integers on an unmanaged heap intz, err := mmm.NewMemChunk(int(0), 10*1e6) if err != nil { log.Fatal(err) } // init our integers for i := 0; i < int(intz.NbObjects()); i++ { intz.Write(i, i) } // get rid of (almost all) previous garbage ints = nil runtime.GC() debug.FreeOSMemory() for i := 0; i < 5; i++ { // randomly print one of our integers to make sure it's all working // as expected (pointer to data is generated on-the-fly) fmt.Printf("\tvalue @ index %d: %d\n", i*1e4, intz.Read(i*1e4)) // run GC now := time.Now().UnixNano() runtime.GC() fmt.Printf("\tGC time (unmanaged heap, pointers generated on-the-fly): %d us\n", (time.Now().UnixNano()-now)/1e3) } // Results: // value @ index 0: 0 // GC time (unmanaged heap, pointers generated on-the-fly): 999 us // value @ index 10000: 10000 // GC time (unmanaged heap, pointers generated on-the-fly): 665 us // value @ index 20000: 20000 // GC time (unmanaged heap, pointers generated on-the-fly): 827 us // value @ index 30000: 30000 // GC time (unmanaged heap, pointers generated on-the-fly): 882 us // value @ index 40000: 40000 // GC time (unmanaged heap, pointers generated on-the-fly): 1016 us fmt.Println() /////////////////////////////////////////////////////// // C: Unmanaged heap, storing all generated pointers // /////////////////////////////////////////////////////// fmt.Println("Case C: what happens when we build and store 10 million pointers for each and every integer in our unmanaged memory chunk?\n") // build 10 million unsafe pointers on the managed heap ptrs := make([]unsafe.Pointer, 10*1e6) // init those pointers so that they point to the unmanaged heap for i := range ptrs { ptrs[i] = unsafe.Pointer(intz.Pointer(i)) } // get rid of (almost all) previous garbage runtime.GC() debug.FreeOSMemory() for i := 0; i < 5; i++ { // randomly print one of our integers to make sure it's all working // as expected fmt.Printf("\tvalue @ index %d: %d\n", i*1e4, *(*int)(ptrs[i*1e4])) // run GC now := time.Now().UnixNano() runtime.GC() fmt.Printf("\tGC time (unmanaged heap, all generated pointers stored): %d us\n", (time.Now().UnixNano()-now)/1e3) } // Results: // value @ index 0: 0 // GC time (unmanaged heap, all generated pointers stored): 47196 us // value @ index 10000: 10000 // GC time (unmanaged heap, all generated pointers stored): 47307 us // value @ index 20000: 20000 // GC time (unmanaged heap, all generated pointers stored): 47485 us // value @ index 30000: 30000 // GC time (unmanaged heap, all generated pointers stored): 47145 us // value @ index 40000: 40000 // GC time (unmanaged heap, all generated pointers stored): 47221 us fmt.Println() /////////////////////////////////////////////////// // D: Unmanaged heap, storing numeric references // /////////////////////////////////////////////////// fmt.Println(`Case D: as case C showed, storing all the generated unsafe pointers to the unmanaged heap is still order of magnitudes faster than storing safe pointers to the managed heap. Still, why keep pointer values when our data is not garbage-collectable? What happens if we store all the generated pointers as numeric references?` + "\n") // build 10 million numeric references on the managed heap refs := make([]uintptr, 10*1e6) // init those references so that they each contain one of the addresses in // our unmanaged heap for i := range refs { refs[i] = uintptr(ptrs[i]) } // get rid of (almost all) previous garbage ptrs = nil runtime.GC() debug.FreeOSMemory() for i := 0; i < 5; i++ { // randomly print one of our integers to make sure it's all working // as expected fmt.Printf("\tvalue @ index %d: %d\n", i*1e4, *(*int)(unsafe.Pointer(refs[i*1e4]))) // run GC now := time.Now().UnixNano() runtime.GC() fmt.Printf("\tGC time (unmanaged heap, all numeric references stored): %d us\n", (time.Now().UnixNano()-now)/1e3) } // Results: // value @ index 0: 0 // GC time (unmanaged heap, all numeric references stored): 715 us // value @ index 10000: 10000 // GC time (unmanaged heap, all numeric references stored): 783 us // value @ index 20000: 20000 // GC time (unmanaged heap, all numeric references stored): 882 us // value @ index 30000: 30000 // GC time (unmanaged heap, all numeric references stored): 711 us // value @ index 40000: 40000 // GC time (unmanaged heap, all numeric references stored): 723 us }