github.com/etecs-ru/ristretto@v0.9.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  
    22  import (
    23  	"fmt"
    24  	"log"
    25  	"math/rand"
    26  	"net/http"
    27  	_ "net/http/pprof"
    28  	"os"
    29  	"os/signal"
    30  	"runtime"
    31  	"sync/atomic"
    32  	"syscall"
    33  	"time"
    34  	"unsafe"
    35  
    36  	"github.com/dustin/go-humanize"
    37  	"github.com/etecs-ru/ristretto/z"
    38  )
    39  
    40  type S struct {
    41  	key  uint64
    42  	val  []byte
    43  	next *S
    44  	inGo bool
    45  }
    46  
    47  var (
    48  	ssz      = int(unsafe.Sizeof(S{}))
    49  	lo, hi   = int64(1 << 30), int64(16 << 30)
    50  	increase = true
    51  	stop     int32
    52  	fill     []byte
    53  	maxMB    = 32
    54  
    55  	cycles int64 = 16
    56  )
    57  
    58  var (
    59  	numbytes int64 //nolint:unused,varcheck,gochecknoglobals,lll,deadcode // adopt fork, do not touch it
    60  	counter  int64 //nolint:unused,varcheck,gochecknoglobals,lll,deadcode // adopt fork, do not touch it
    61  )
    62  
    63  func newS(sz int) *S {
    64  	var s *S
    65  	if b := Calloc(ssz); len(b) > 0 {
    66  		s = (*S)(unsafe.Pointer(&b[0]))
    67  	} else {
    68  		s = &S{inGo: true}
    69  	}
    70  
    71  	s.val = Calloc(sz)
    72  	copy(s.val, fill)
    73  	if s.next != nil {
    74  		log.Fatalf("news.next must be nil: %p", s.next)
    75  	}
    76  	return s
    77  }
    78  
    79  func freeS(s *S) {
    80  	Free(s.val)
    81  	if !s.inGo {
    82  		buf := (*[z.MaxArrayLen]byte)(unsafe.Pointer(s))[:ssz:ssz]
    83  		Free(buf)
    84  	}
    85  }
    86  
    87  func (s *S) allocateNext(sz int) {
    88  	ns := newS(sz)
    89  	s.next, ns.next = ns, s.next
    90  }
    91  
    92  func (s *S) deallocNext() {
    93  	if s.next == nil {
    94  		log.Fatal("next should not be nil")
    95  	}
    96  	next := s.next
    97  	s.next = next.next
    98  	freeS(next)
    99  }
   100  
   101  func memory() {
   102  	// In normal mode, z.NumAllocBytes would always be zero. So, this program would misbehave.
   103  	curMem := NumAllocBytes()
   104  	if increase {
   105  		if curMem > hi {
   106  			increase = false
   107  		}
   108  	} else {
   109  		if curMem < lo {
   110  			increase = true
   111  			runtime.GC()
   112  			time.Sleep(3 * time.Second)
   113  
   114  			counter++
   115  		}
   116  	}
   117  	var js z.MemStats
   118  	z.ReadMemStats(&js)
   119  
   120  	fmt.Printf("[%d] Current Memory: %s. Increase? %v, MemStats [Active: %s, Allocated: %s,"+
   121  		" Resident: %s, Retained: %s]\n",
   122  		counter, humanize.IBytes(uint64(curMem)), increase,
   123  		humanize.IBytes(js.Active), humanize.IBytes(js.Allocated),
   124  		humanize.IBytes(js.Resident), humanize.IBytes(js.Retained))
   125  }
   126  
   127  func viaLL() {
   128  	ticker := time.NewTicker(10 * time.Millisecond)
   129  	defer ticker.Stop()
   130  
   131  	root := newS(1)
   132  	for range ticker.C {
   133  		if counter >= cycles {
   134  			fmt.Printf("Finished %d cycles. Deallocating...\n", counter)
   135  			break
   136  		}
   137  		if atomic.LoadInt32(&stop) == 1 {
   138  			break
   139  		}
   140  		if increase {
   141  			root.allocateNext(rand.Intn(maxMB) << 20)
   142  		} else {
   143  			root.deallocNext()
   144  		}
   145  		memory()
   146  	}
   147  	for root.next != nil {
   148  		root.deallocNext()
   149  		memory()
   150  	}
   151  	freeS(root)
   152  }
   153  
   154  const timeout = 5 * time.Second
   155  
   156  func main() {
   157  	check()
   158  	fill = make([]byte, maxMB<<20)
   159  	rand.Read(fill)
   160  
   161  	c := make(chan os.Signal)
   162  	signal.Notify(c, os.Interrupt, syscall.SIGTERM)
   163  	go func() {
   164  		<-c
   165  		fmt.Println("Stopping")
   166  		atomic.StoreInt32(&stop, 1)
   167  	}()
   168  	go func() {
   169  		if err := http.ListenAndServe("0.0.0.0:8080", nil); err != nil {
   170  			log.Fatalf("Error: %v", err)
   171  		}
   172  	}()
   173  
   174  	viaLL()
   175  	if left := NumAllocBytes(); left != 0 {
   176  		log.Fatalf("Unable to deallocate all memory: %v\n", left)
   177  	}
   178  	runtime.GC()
   179  	fmt.Println("Done. Reduced to zero memory usage.")
   180  	time.Sleep(timeout)
   181  }