github.com/codingeasygo/util@v0.0.0-20231206062002-1ce2f004b7d9/xtest/perf2.go (about) 1 package xtest 2 3 import ( 4 "bytes" 5 "encoding/json" 6 "fmt" 7 "io/ioutil" 8 "log" 9 "math" 10 "net/http" 11 "os" 12 "sort" 13 "strconv" 14 "strings" 15 "sync" 16 "sync/atomic" 17 "time" 18 19 "github.com/codingeasygo/util/xtime" 20 ) 21 22 func AutoExec2(logf string, precall func(state Perf2, ridx uint64, used int64, rerr error) (uint64, error), call func(uint64) error) (used int64, max, avg uint64, err error) { 23 var total, tc, peradd, runmax uint64 = 1000000, 100, 2, 100000 24 var pretimeout int64 = 1000 25 total, err = strconv.ParseUint(os.Getenv("PERF_TOTOAL"), 10, 64) 26 if err != nil { 27 total = 1000000 28 } 29 tc, err = strconv.ParseUint(os.Getenv("PERF_TC"), 10, 64) 30 if err != nil { 31 tc = 100 32 } 33 peradd, err = strconv.ParseUint(os.Getenv("PERF_PERADD"), 10, 64) 34 if err != nil { 35 peradd = 2 36 } 37 pretimeout, err = strconv.ParseInt(os.Getenv("PERF_TIMEOUT"), 10, 64) 38 if err != nil { 39 pretimeout = 1000 40 } 41 runmax, err = strconv.ParseUint(os.Getenv("PERF_MAX"), 10, 64) 42 if err != nil { 43 runmax = 100000 44 } 45 fmt.Printf("AutoExec:\n\ttotal:%v,tc:%v,peradd:%v,timeout:%v\n\n", total, tc, peradd, pretimeout) 46 perf := NewPerf2() 47 perf.ShowState = true 48 perf.RunninMax = runmax 49 perf.Timeout = pretimeout 50 // perf.ExternalStatus = func() string { 51 // mgo.Plck.Lock() 52 // defer mgo.Plck.Unlock() 53 // return fmt.Sprintf(",%v,%v", len(mgo.Pending), mgo.Donc) 54 // } 55 return perf.AutoExec(total, tc, peradd, logf, precall, call) 56 } 57 58 type Perf2 struct { 59 Running int64 60 Max uint64 61 Avg uint64 62 PerUsed map[float64]int64 63 PerUsedMax int64 64 PerUsedMin int64 65 PerUsedAvg int64 66 PerUsedAll int64 67 Done int64 68 Errc int64 69 Used int64 70 lck *sync.RWMutex 71 mrunning bool 72 mwait *sync.WaitGroup 73 stdout, stderr *os.File 74 ShowState bool `json:"-"` 75 IncreaseDelay int64 76 ExternalStatus func() string 77 RunninMax uint64 78 timeouted uint64 79 fulled uint64 80 Timeout int64 81 } 82 83 func NewPerf2() *Perf2 { 84 return &Perf2{ 85 lck: &sync.RWMutex{}, 86 mwait: &sync.WaitGroup{}, 87 IncreaseDelay: 200, 88 ExternalStatus: func() string { return "" }, 89 RunninMax: 8000, 90 PerUsed: map[float64]int64{}, 91 } 92 } 93 94 type DisVal []string 95 96 func (d DisVal) Len() int { 97 return len(d) 98 } 99 100 func (d DisVal) Swap(i, j int) { 101 d[i], d[j] = d[j], d[i] 102 } 103 104 func (d DisVal) Less(i, j int) bool { 105 vali, _ := strconv.ParseFloat(strings.Split(d[i], ":")[0], 64) 106 valj, _ := strconv.ParseFloat(strings.Split(d[j], ":")[0], 64) 107 return vali < valj 108 } 109 110 func (p *Perf2) String() string { 111 p.lck.Lock() 112 dis := DisVal{} 113 for d, v := range p.PerUsed { 114 dis = append(dis, fmt.Sprintf("%v:%v%%", d, int64(100*float64(v)/float64(p.Done)))) 115 } 116 p.lck.Unlock() 117 sort.Sort(dis) 118 alldis := strings.Join(dis, ", ") 119 external := p.ExternalStatus() 120 if len(external) > 0 { 121 alldis += "\n " + external 122 } 123 // alldis += "\n " + fmt.Sprintf("Max:%v", mgo.UserLockPending2.Max) 124 p.NotifyReal() 125 return fmt.Sprintf("Used:%v,Done:%v,Errc:%v,TPS:%v,Running:%v,Max:%v,Avg:%v,PerMax:%v,PerMin:%v,PerAvg:%v,Timeout:%v,Fullc:%v\n %v", 126 p.Used, p.Done, p.Errc, p.Done*1000/p.Used, p.Running, p.Max, p.Avg, p.PerUsedMax, p.PerUsedMin, p.PerUsedAvg, p.timeouted, p.fulled, alldis) 127 } 128 129 func (p *Perf2) NotifyReal() { 130 realURI := os.Getenv("REAL_URI") 131 if len(realURI) > 0 { 132 host := os.Getenv("HOSTNAME") 133 host = strings.Split(host, ".")[0] 134 bys, _ := json.Marshal(map[string]map[string]interface{}{ 135 host: { 136 "Used": p.Used, 137 "Done": p.Done, 138 "Errc": p.Errc, 139 "TPS": p.Done * 1000 / p.Used, 140 "Running": p.Running, 141 "Max": p.Max, 142 "Avg": p.Avg, 143 "PerUsedMax": p.PerUsedMax, 144 "PerUsedAvg": p.PerUsedAvg, 145 "Timeout": p.timeouted, 146 "Fullc": p.fulled, 147 }, 148 }) 149 resp, err := http.Post(realURI, "application/json", bytes.NewBuffer(bys)) 150 if err == nil { 151 ioutil.ReadAll(resp.Body) 152 } 153 } 154 } 155 156 func (p *Perf2) AutoExec(total, tc, peradd uint64, logf string, precall func(state Perf2, ridx uint64, used int64, rerr error) (uint64, error), call func(uint64) error) (used int64, max, avg uint64, err error) { 157 // percallc := 1 158 used, err = p.Exec(total, tc, logf, 159 func(state Perf2, ridx uint64, used int64, rerr error) (uint64, error) { 160 // time.Sleep(time.Second) 161 _, terr := precall(state, ridx, used, rerr) 162 if IsFullError(terr) { 163 atomic.AddUint64(&p.fulled, 1) 164 if uint64(p.Running) < tc { 165 return 1, nil 166 } 167 return 0, nil 168 } 169 if terr != nil { 170 return 0, terr 171 } 172 if p.Timeout > 0 && used >= p.Timeout { 173 atomic.AddUint64(&p.timeouted, 1) 174 if uint64(p.Running) < tc { 175 return 1, nil 176 } 177 return 0, nil 178 } 179 if state.Running > int64(p.RunninMax) { 180 return 0, nil 181 } 182 return peradd, nil 183 // beg := utils.Now() 184 // terr := precall(idx, state) 185 // if terr == nil { 186 // if state.Running >= p.RunninMax { 187 // return 0, nil 188 // } 189 // pretimeout := utils.Now()-beg < pretimeout 190 // if pretimeout { 191 // percallc++ 192 // nadd := peradd*percallc - int(state.Running) 193 // return nadd, nil 194 // } 195 // p.pretimeouted = pretimeout 196 // if int(state.Running) < tc { 197 // return 1, nil 198 // } 199 // return 0, nil 200 // } else if terr == FullError { 201 // return 0, nil 202 // } else { 203 // return 0, terr 204 // } 205 }, call) 206 207 max, avg = p.Max, p.Avg 208 fmt.Printf(` 209 %v 210 ------Done------ 211 - total:%v 212 - max:%v 213 - avg:%v 214 - used:%v 215 - error:%v 216 ---------------------- 217 %v`, p.String(), total, max, avg, used, err, "\n") 218 return 219 } 220 221 func (p *Perf2) perdone(perused int64) { 222 p.lck.Lock() 223 defer p.lck.Unlock() 224 p.Done++ 225 p.PerUsedAll += perused 226 p.PerUsedAvg = p.PerUsedAll / p.Done 227 if p.PerUsedMax < perused { 228 p.PerUsedMax = perused 229 } 230 if p.PerUsedMin == 0 || p.PerUsedMin > perused { 231 p.PerUsedMin = perused 232 } 233 if p.Timeout > 0 { 234 multiple := float64(perused) / float64(p.Timeout) 235 if multiple <= 0.25 { 236 p.PerUsed[0.25]++ 237 } else if multiple <= 0.5 { 238 p.PerUsed[0.5]++ 239 } else if multiple <= 0.75 { 240 p.PerUsed[0.75]++ 241 } else if multiple <= 1 { 242 p.PerUsed[1]++ 243 } else if multiple <= 1.5 { 244 p.PerUsed[1.5]++ 245 } else if multiple <= 2 { 246 p.PerUsed[2]++ 247 } else { 248 floatn := (math.Sqrt(8*multiple+1) - 1) / 2 249 n := float64(int64(floatn)) 250 if n < floatn { 251 n++ 252 } 253 p.PerUsed[(n-1)*(n+2)/2+1]++ 254 } 255 } 256 } 257 func (p *Perf2) monitor() { 258 var allrunning, allc uint64 259 p.mrunning = true 260 p.mwait.Add(1) 261 defer p.mwait.Done() 262 263 beg := xtime.Now() 264 for p.mrunning { 265 running := uint64(p.Running) 266 allrunning += running 267 allc++ 268 p.Avg = allrunning / allc 269 if p.Max < running { 270 p.Max = running 271 } 272 p.Used = xtime.Now() - beg 273 if p.ShowState { 274 fmt.Fprintf(p.stdout, "->%v\n", p) 275 } 276 time.Sleep(time.Second) 277 } 278 } 279 280 func (p *Perf2) Exec(total, tc uint64, logf string, increase func(state Perf2, ridx uint64, used int64, rerr error) (uint64, error), call func(uint64) error) (int64, error) { 281 p.stdout, p.stderr = os.Stdout, os.Stderr 282 if len(logf) > 0 { 283 f, err := os.OpenFile(logf, os.O_APPEND|os.O_CREATE|os.O_WRONLY, os.ModePerm) 284 if err != nil { 285 return 0, err 286 } 287 os.Stdout = f 288 os.Stderr = f 289 log.SetOutput(f) 290 } 291 if tc > total { 292 tc = total 293 } 294 go p.monitor() 295 ws := sync.WaitGroup{} 296 // ws.Add(total) 297 beg := xtime.Now() 298 var tidx_ uint64 = 0 299 var run_call func(ridx uint64) 300 var run_next func(ridx uint64, used int64, rerr error) 301 var err error = nil 302 run_call = func(ridx uint64) { 303 defer ws.Done() 304 305 perbeg := xtime.Now() 306 terr := call(ridx) 307 perused := xtime.Now() - perbeg 308 if terr == nil { 309 p.perdone(perused) 310 } else { 311 atomic.AddInt64(&p.Errc, 1) 312 } 313 run_next(ridx, perused, terr) 314 atomic.AddInt64(&p.Running, -1) 315 316 } 317 // increaseLck := sync.RWMutex{} 318 run_next = func(ridx uint64, used int64, rerr error) { 319 // increaseLck.Lock() 320 nc, terr := increase(*p, ridx, used, rerr) 321 // increaseLck.Unlock() 322 if terr != nil { 323 err = terr 324 return 325 } 326 for i := uint64(0); i < nc; i++ { 327 ridx := atomic.AddUint64(&tidx_, 1) 328 if ridx >= total { 329 break 330 } 331 ws.Add(1) 332 atomic.AddInt64(&p.Running, 1) 333 go run_call(ridx) 334 } 335 } 336 atomic.AddUint64(&tidx_, tc-1) 337 for i := uint64(0); i < tc; i++ { 338 ws.Add(1) 339 atomic.AddInt64(&p.Running, 1) 340 go run_call(i) 341 } 342 // for err == nil && tidx_ < int32(total) { 343 // time.Sleep(time.Duration(p.IncreaseDelay) * time.Millisecond) 344 // run_next(0) 345 // } 346 ws.Wait() 347 end := xtime.Now() 348 if len(logf) > 0 { 349 os.Stdout.Close() 350 os.Stdout = p.stdout 351 os.Stderr = p.stderr 352 log.SetOutput(os.Stdout) 353 } 354 p.mrunning = false 355 p.mwait.Wait() 356 return end - beg, err 357 } 358 359 func TimeoutSec(state Perf2, ridx uint64, used int64, rerr error) (uint64, error) { 360 if rerr != nil { 361 return 0, rerr 362 } 363 if used > 1000 { 364 return 0, NewFullError(fmt.Errorf("time out")) 365 } 366 return 1, nil 367 }