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  }