github.com/teh-cmc/mmm@v0.0.0-20160717174312-f3d5d92d1c27/experiment/experiment.go (about) 1 // Copyright © 2015 Clement 'cmc' Rey <cr.rey.clement@gmail.com>. 2 // 3 // Use of this source code is governed by an MIT-style 4 // license that can be found in the LICENSE file. 5 6 package main 7 8 import ( 9 "fmt" 10 "log" 11 "runtime" 12 "runtime/debug" 13 "time" 14 "unsafe" 15 16 "github.com/teh-cmc/mmm" 17 ) 18 19 // ----------------------------------------------------------------------------- 20 21 ///// 22 // This file shows various ways of using mmm's MemChunks and how these ways 23 // affect GC's performances compared to native Go pointers. 24 // 25 // All of the results shown below were computed using a DELL XPS 15-9530 26 // (i7-4712HQ@2.30GHz). 27 // 28 // go run experiment.go 29 // 30 ///// 31 32 func main() { 33 34 ///////////////////////////////////////////////// 35 // A: Managed heap, 10 million pointers to int // 36 ///////////////////////////////////////////////// 37 fmt.Println(`Case A: what happens when we store 10 million pointers to integer 38 on the managed heap?` + "\n") 39 40 // build 10 million pointers to integer on the managed heap 41 ints := make([]*int, 10*1e6) 42 // init our pointers 43 for i := range ints { 44 j := i 45 ints[i] = &j 46 } 47 48 // get rid of init garbage 49 runtime.GC() 50 debug.FreeOSMemory() 51 52 for i := 0; i < 5; i++ { 53 // randomly print one of our integers to make sure it's all working 54 // as expected, and to prevent them from being optimized away 55 fmt.Printf("\tvalue @ index %d: %d\n", i*1e4, *(ints[i*1e4])) 56 57 // run GC 58 now := time.Now().UnixNano() 59 runtime.GC() 60 fmt.Printf("\tGC time (managed heap, 10 million pointers): %d us\n", (time.Now().UnixNano()-now)/1e3) 61 } 62 63 // Results: 64 // value @ index 0: 0 65 // GC time (managed heap, 10 million pointers): 329840 us 66 // value @ index 10000: 10000 67 // GC time (managed heap, 10 million pointers): 325375 us 68 // value @ index 20000: 20000 69 // GC time (managed heap, 10 million pointers): 323367 us 70 // value @ index 30000: 30000 71 // GC time (managed heap, 10 million pointers): 327905 us 72 // value @ index 40000: 40000 73 // GC time (managed heap, 10 million pointers): 326469 us 74 75 fmt.Println() 76 77 ////////////////////////////////////////////////////// 78 // B: Unmanaged heap, pointers generated on-the-fly // 79 ////////////////////////////////////////////////////// 80 fmt.Println(`Case B: mmm doesn't store any pointer, it doesn't need to. 81 Since the data is stored on an unmanaged heap, it cannot be collected 82 even if there's no reference to it. This allows mmm to generate 83 pointers only when something's actually reading or writing to the data.` + "\n") 84 85 // build 10 million integers on an unmanaged heap 86 intz, err := mmm.NewMemChunk(int(0), 10*1e6) 87 if err != nil { 88 log.Fatal(err) 89 } 90 // init our integers 91 for i := 0; i < int(intz.NbObjects()); i++ { 92 intz.Write(i, i) 93 } 94 95 // get rid of (almost all) previous garbage 96 ints = nil 97 runtime.GC() 98 debug.FreeOSMemory() 99 100 for i := 0; i < 5; i++ { 101 // randomly print one of our integers to make sure it's all working 102 // as expected (pointer to data is generated on-the-fly) 103 fmt.Printf("\tvalue @ index %d: %d\n", i*1e4, intz.Read(i*1e4)) 104 105 // run GC 106 now := time.Now().UnixNano() 107 runtime.GC() 108 fmt.Printf("\tGC time (unmanaged heap, pointers generated on-the-fly): %d us\n", (time.Now().UnixNano()-now)/1e3) 109 } 110 111 // Results: 112 // value @ index 0: 0 113 // GC time (unmanaged heap, pointers generated on-the-fly): 999 us 114 // value @ index 10000: 10000 115 // GC time (unmanaged heap, pointers generated on-the-fly): 665 us 116 // value @ index 20000: 20000 117 // GC time (unmanaged heap, pointers generated on-the-fly): 827 us 118 // value @ index 30000: 30000 119 // GC time (unmanaged heap, pointers generated on-the-fly): 882 us 120 // value @ index 40000: 40000 121 // GC time (unmanaged heap, pointers generated on-the-fly): 1016 us 122 123 fmt.Println() 124 125 /////////////////////////////////////////////////////// 126 // C: Unmanaged heap, storing all generated pointers // 127 /////////////////////////////////////////////////////// 128 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") 129 130 // build 10 million unsafe pointers on the managed heap 131 ptrs := make([]unsafe.Pointer, 10*1e6) 132 // init those pointers so that they point to the unmanaged heap 133 for i := range ptrs { 134 ptrs[i] = unsafe.Pointer(intz.Pointer(i)) 135 } 136 137 // get rid of (almost all) previous garbage 138 runtime.GC() 139 debug.FreeOSMemory() 140 141 for i := 0; i < 5; i++ { 142 // randomly print one of our integers to make sure it's all working 143 // as expected 144 fmt.Printf("\tvalue @ index %d: %d\n", i*1e4, *(*int)(ptrs[i*1e4])) 145 146 // run GC 147 now := time.Now().UnixNano() 148 runtime.GC() 149 fmt.Printf("\tGC time (unmanaged heap, all generated pointers stored): %d us\n", (time.Now().UnixNano()-now)/1e3) 150 } 151 152 // Results: 153 // value @ index 0: 0 154 // GC time (unmanaged heap, all generated pointers stored): 47196 us 155 // value @ index 10000: 10000 156 // GC time (unmanaged heap, all generated pointers stored): 47307 us 157 // value @ index 20000: 20000 158 // GC time (unmanaged heap, all generated pointers stored): 47485 us 159 // value @ index 30000: 30000 160 // GC time (unmanaged heap, all generated pointers stored): 47145 us 161 // value @ index 40000: 40000 162 // GC time (unmanaged heap, all generated pointers stored): 47221 us 163 164 fmt.Println() 165 166 /////////////////////////////////////////////////// 167 // D: Unmanaged heap, storing numeric references // 168 /////////////////////////////////////////////////// 169 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. 170 Still, why keep pointer values when our data is not garbage-collectable? 171 What happens if we store all the generated pointers as numeric references?` + "\n") 172 173 // build 10 million numeric references on the managed heap 174 refs := make([]uintptr, 10*1e6) 175 // init those references so that they each contain one of the addresses in 176 // our unmanaged heap 177 for i := range refs { 178 refs[i] = uintptr(ptrs[i]) 179 } 180 181 // get rid of (almost all) previous garbage 182 ptrs = nil 183 runtime.GC() 184 debug.FreeOSMemory() 185 186 for i := 0; i < 5; i++ { 187 // randomly print one of our integers to make sure it's all working 188 // as expected 189 fmt.Printf("\tvalue @ index %d: %d\n", i*1e4, *(*int)(unsafe.Pointer(refs[i*1e4]))) 190 191 // run GC 192 now := time.Now().UnixNano() 193 runtime.GC() 194 fmt.Printf("\tGC time (unmanaged heap, all numeric references stored): %d us\n", (time.Now().UnixNano()-now)/1e3) 195 } 196 197 // Results: 198 // value @ index 0: 0 199 // GC time (unmanaged heap, all numeric references stored): 715 us 200 // value @ index 10000: 10000 201 // GC time (unmanaged heap, all numeric references stored): 783 us 202 // value @ index 20000: 20000 203 // GC time (unmanaged heap, all numeric references stored): 882 us 204 // value @ index 30000: 30000 205 // GC time (unmanaged heap, all numeric references stored): 711 us 206 // value @ index 40000: 40000 207 // GC time (unmanaged heap, all numeric references stored): 723 us 208 }