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 }