github.com/angenalZZZ/gofunc@v0.0.0-20210507121333-48ff1be3917b/f/times.go (about) 1 package f 2 3 import ( 4 "fmt" 5 "io" 6 "sync" 7 "time" 8 ) 9 10 // At Runs fn at the specified time. 11 func At(t time.Time, fn func()) (done <-chan bool) { 12 return After(t.Sub(time.Now()), fn) 13 } 14 15 // Until Runs until time in every dur. 16 func Until(t time.Time, dur time.Duration, fn func()) (done <-chan bool) { 17 ch := make(chan bool, 1) 18 untilRecv(ch, t, dur, fn) 19 return ch 20 } 21 22 func untilRecv(ch chan bool, t time.Time, dur time.Duration, fn func()) { 23 if t.Sub(time.Now()) > 0 { 24 time.AfterFunc(dur, func() { 25 fn() 26 untilRecv(ch, t, dur, fn) 27 }) 28 return 29 } 30 doneSig(ch, true) 31 } 32 33 // After Runs fn after duration. Similar to time.AfterFunc 34 func After(duration time.Duration, fn func()) (done <-chan bool) { 35 ch := make(chan bool, 1) 36 time.AfterFunc(duration, func() { 37 fn() 38 doneSig(ch, true) 39 }) 40 return ch 41 } 42 43 // Every Runs fn in every specified duration. 44 func Every(dur time.Duration, fn func()) { 45 time.AfterFunc(dur, func() { 46 fn() 47 Every(dur, fn) 48 }) 49 } 50 51 // Timeout Runs fn and times out if it runs longer than the provided 52 // duration. It will send false to the returning 53 // channel if timeout occurs. 54 func Timeout(duration time.Duration, fn func()) (done <-chan bool) { 55 ch := make(chan bool, 2) 56 go func() { 57 <-time.After(duration) 58 doneSig(ch, false) 59 }() 60 go func() { 61 fn() 62 doneSig(ch, true) 63 }() 64 return ch 65 } 66 67 // All Starts to run the given list of fns concurrently. 68 func All(fns ...func()) (done <-chan bool) { 69 var wg sync.WaitGroup 70 wg.Add(len(fns)) 71 72 ch := make(chan bool, 1) 73 for _, fn := range fns { 74 go func(f func()) { 75 f() 76 wg.Done() 77 }(fn) 78 } 79 go func() { 80 wg.Wait() 81 doneSig(ch, true) 82 }() 83 return ch 84 } 85 86 // AllWithThrottle Starts to run the given list of fns concurrently, 87 // at most n fns at a time. 88 func AllWithThrottle(throttle int, fns ...func()) (done <-chan bool) { 89 ch := make(chan bool, 1) 90 go func() { 91 for { 92 num := throttle 93 if throttle > len(fns) { 94 num = len(fns) 95 } 96 next := fns[:num] 97 fns = fns[num:] 98 <-All(next...) 99 if len(fns) == 0 { 100 doneSig(ch, true) 101 break 102 } 103 } 104 }() 105 return ch 106 } 107 108 // Replicate Run the same function with n copies. 109 func Replicate(n int, fn func()) (done <-chan bool) { 110 fns := make([]func(), n) 111 for i := 0; i < n; i++ { 112 fns[i] = fn 113 } 114 return All(fns...) 115 } 116 117 func doneSig(ch chan bool, val bool) { 118 ch <- val 119 close(ch) 120 } 121 122 // Ops executed a number of operations over a multiple goroutines. 123 // count is the number of operations. 124 // threads is the number goroutines. 125 // op is the operation function 126 func Ops(count, threads int, op func(i, thread int)) { 127 var start time.Time 128 var wg sync.WaitGroup 129 wg.Add(threads) 130 output := Output 131 if output != nil { 132 start = time.Now() 133 } 134 for i := 0; i < threads; i++ { 135 s, e := count/threads*i, count/threads*(i+1) 136 if i == threads-1 { 137 e = count 138 } 139 go func(i, s, e int) { 140 defer wg.Done() 141 for j := s; j < e; j++ { 142 op(j, i) 143 } 144 }(i, s, e) 145 } 146 wg.Wait() 147 148 if output != nil { 149 dur := time.Since(start) 150 var alloc uint64 151 WriteOutput(output, count, threads, dur, alloc) 152 } 153 } 154 155 func opsComma(n int) string { 156 s1, s2 := fmt.Sprintf("%d", n), "" 157 for i, j := len(s1)-1, 0; i >= 0; i, j = i-1, j+1 { 158 if j%3 == 0 && j != 0 { 159 s2 = "," + s2 160 } 161 s2 = string(s1[i]) + s2 162 } 163 return s2 164 } 165 166 func opsMem(alloc uint64) string { 167 switch { 168 case alloc <= 1024: 169 return fmt.Sprintf("%d bytes", alloc) 170 case alloc <= 1024*1024: 171 return fmt.Sprintf("%.1f KB", float64(alloc)/1024) 172 case alloc <= 1024*1024*1024: 173 return fmt.Sprintf("%.1f MB", float64(alloc)/1024/1024) 174 default: 175 return fmt.Sprintf("%.1f GB", float64(alloc)/1024/1024/1024) 176 } 177 } 178 179 // WriteOutput writes an output line to the specified writer 180 func WriteOutput(w io.Writer, count, threads int, elapsed time.Duration, alloc uint64) { 181 var ss string 182 if threads != 1 { 183 ss = fmt.Sprintf("over %d threads ", threads) 184 } 185 var qps int 186 if count > 0 { 187 qps = int(elapsed / time.Duration(count)) 188 } 189 var allocStr string 190 if alloc > 0 { 191 var bops uint64 192 if count > 0 { 193 bops = alloc / uint64(count) 194 } 195 allocStr = fmt.Sprintf(", %s, %d bytes/op", opsMem(alloc), bops) 196 } 197 _, _ = fmt.Fprintf(w, "%s ops %sin %.0fms, %s/sec, %d ns/op%s\n", 198 opsComma(count), ss, elapsed.Seconds()*1000, 199 opsComma(int(float64(count)/elapsed.Seconds())), 200 qps, allocStr, 201 ) 202 }