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  }