github.com/fiatjaf/generic-ristretto@v0.0.1/contrib/memtest/main.go (about)

     1  /*
     2   * Copyright 2020 Dgraph Labs, Inc. and Contributors
     3   *
     4   * Licensed under the Apache License, Version 2.0 (the "License");
     5   * you may not use this file except in compliance with the License.
     6   * You may obtain a copy of the License at
     7   *
     8   *     http://www.apache.org/licenses/LICENSE-2.0
     9   *
    10   * Unless required by applicable law or agreed to in writing, software
    11   * distributed under the License is distributed on an "AS IS" BASIS,
    12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13   * See the License for the specific language governing permissions and
    14   * limitations under the License.
    15   */
    16  
    17  package main
    18  
    19  // #include <stdlib.h>
    20  import "C"
    21  import (
    22  	"fmt"
    23  	"math/rand"
    24  	"net/http"
    25  	_ "net/http/pprof"
    26  	"os"
    27  	"os/signal"
    28  	"runtime"
    29  	"sync/atomic"
    30  	"syscall"
    31  	"time"
    32  	"unsafe"
    33  
    34  	"github.com/fiatjaf/generic-ristretto/z"
    35  	"github.com/dustin/go-humanize"
    36  	"github.com/golang/glog"
    37  )
    38  
    39  type S struct {
    40  	key  uint64
    41  	val  []byte
    42  	next *S
    43  	inGo bool
    44  }
    45  
    46  var (
    47  	ssz      = int(unsafe.Sizeof(S{}))
    48  	lo, hi   = int64(1 << 30), int64(16 << 30)
    49  	increase = true
    50  	stop     int32
    51  	fill     []byte
    52  	maxMB    = 32
    53  
    54  	cycles int64 = 16
    55  )
    56  var numbytes int64
    57  var counter int64
    58  
    59  func newS(sz int) *S {
    60  	var s *S
    61  	if b := Calloc(ssz); len(b) > 0 {
    62  		s = (*S)(unsafe.Pointer(&b[0]))
    63  	} else {
    64  		s = &S{inGo: true}
    65  	}
    66  
    67  	s.val = Calloc(sz)
    68  	copy(s.val, fill)
    69  	if s.next != nil {
    70  		glog.Fatalf("news.next must be nil: %p", s.next)
    71  	}
    72  	return s
    73  }
    74  
    75  func freeS(s *S) {
    76  	Free(s.val)
    77  	if !s.inGo {
    78  		buf := (*[z.MaxArrayLen]byte)(unsafe.Pointer(s))[:ssz:ssz]
    79  		Free(buf)
    80  	}
    81  }
    82  
    83  func (s *S) allocateNext(sz int) {
    84  	ns := newS(sz)
    85  	s.next, ns.next = ns, s.next
    86  }
    87  
    88  func (s *S) deallocNext() {
    89  	if s.next == nil {
    90  		glog.Fatal("next should not be nil")
    91  	}
    92  	next := s.next
    93  	s.next = next.next
    94  	freeS(next)
    95  }
    96  
    97  func memory() {
    98  	// In normal mode, z.NumAllocBytes would always be zero. So, this program would misbehave.
    99  	curMem := NumAllocBytes()
   100  	if increase {
   101  		if curMem > hi {
   102  			increase = false
   103  		}
   104  	} else {
   105  		if curMem < lo {
   106  			increase = true
   107  			runtime.GC()
   108  			time.Sleep(3 * time.Second)
   109  
   110  			counter++
   111  		}
   112  	}
   113  	var js z.MemStats
   114  	z.ReadMemStats(&js)
   115  
   116  	fmt.Printf("[%d] Current Memory: %s. Increase? %v, MemStats [Active: %s, Allocated: %s,"+
   117  		" Resident: %s, Retained: %s]\n",
   118  		counter, humanize.IBytes(uint64(curMem)), increase,
   119  		humanize.IBytes(js.Active), humanize.IBytes(js.Allocated),
   120  		humanize.IBytes(js.Resident), humanize.IBytes(js.Retained))
   121  }
   122  
   123  func viaLL() {
   124  	ticker := time.NewTicker(10 * time.Millisecond)
   125  	defer ticker.Stop()
   126  
   127  	root := newS(1)
   128  	for range ticker.C {
   129  		if counter >= cycles {
   130  			fmt.Printf("Finished %d cycles. Deallocating...\n", counter)
   131  			break
   132  		}
   133  		if atomic.LoadInt32(&stop) == 1 {
   134  			break
   135  		}
   136  		if increase {
   137  			root.allocateNext(rand.Intn(maxMB) << 20)
   138  		} else {
   139  			root.deallocNext()
   140  		}
   141  		memory()
   142  	}
   143  	for root.next != nil {
   144  		root.deallocNext()
   145  		memory()
   146  	}
   147  	freeS(root)
   148  }
   149  
   150  func main() {
   151  	check()
   152  	fill = make([]byte, maxMB<<20)
   153  	rand.Read(fill)
   154  
   155  	c := make(chan os.Signal)
   156  	signal.Notify(c, os.Interrupt, syscall.SIGTERM)
   157  	go func() {
   158  		<-c
   159  		fmt.Println("Stopping")
   160  		atomic.StoreInt32(&stop, 1)
   161  	}()
   162  	go func() {
   163  		if err := http.ListenAndServe("0.0.0.0:8080", nil); err != nil {
   164  			glog.Fatalf("Error: %v", err)
   165  		}
   166  	}()
   167  
   168  	viaLL()
   169  	if left := NumAllocBytes(); left != 0 {
   170  		glog.Fatalf("Unable to deallocate all memory: %v\n", left)
   171  	}
   172  	runtime.GC()
   173  	fmt.Println("Done. Reduced to zero memory usage.")
   174  	time.Sleep(5 * time.Second)
   175  }