github.com/loov/combiner@v0.1.0/example/append.go (about)

     1  // +build ignore
     2  
     3  package main
     4  
     5  import (
     6  	"fmt"
     7  	"os"
     8  	"runtime"
     9  	"sync"
    10  	"time"
    11  
    12  	"github.com/loov/combiner"
    13  	"github.com/loov/hrtime"
    14  )
    15  
    16  const (
    17  	P = 100
    18  	N = 100
    19  )
    20  
    21  func main() {
    22  	f, _ := os.Create("temp.dat~")
    23  	f.Truncate(P * N)
    24  
    25  	f.Seek(0, os.SEEK_SET)
    26  	fmt.Println("CombiningFile", Bench(NewCombiningFile(f)))
    27  
    28  	cfile := NewChanFile(f)
    29  	cfile.Start()
    30  	f.Seek(0, os.SEEK_SET)
    31  	fmt.Println("ChanFile", Bench(cfile))
    32  	cfile.Stop()
    33  
    34  	f.Seek(0, os.SEEK_SET)
    35  	fmt.Println("MutexFile", Bench(NewMutexFile(f)))
    36  
    37  	// Output:
    38  	// CombiningFile 161.179503ms
    39  	// ChanFile 220.223679ms
    40  	// MutexFile 10.987153817s
    41  }
    42  
    43  type Writer interface {
    44  	WriteByte(byte byte)
    45  }
    46  
    47  func Bench(w Writer) time.Duration {
    48  	start := hrtime.TSC()
    49  
    50  	var wg sync.WaitGroup
    51  	wg.Add(P)
    52  	for i := 0; i < P; i++ {
    53  		go func(pid int) {
    54  			for i := 0; i < N; i++ {
    55  				w.WriteByte(byte(i))
    56  			}
    57  			wg.Done()
    58  		}(i)
    59  	}
    60  	wg.Wait()
    61  
    62  	stop := hrtime.TSC()
    63  	return (stop - start).ApproxDuration()
    64  }
    65  
    66  type CombiningFile struct {
    67  	add  combiner.Parking
    68  	file *os.File
    69  }
    70  type combiningAppend CombiningFile
    71  
    72  func NewCombiningFile(f *os.File) *CombiningFile {
    73  	m := &CombiningFile{}
    74  	m.add.Init((*combiningAppend)(m), 100)
    75  	m.file = f
    76  	return m
    77  }
    78  
    79  func (m *CombiningFile) WriteByte(b byte) { m.add.Do(b) }
    80  
    81  func (m *combiningAppend) Start()             { runtime.Gosched() }
    82  func (m *combiningAppend) Do(arg interface{}) { m.file.Write([]byte{arg.(byte)}) }
    83  func (m *combiningAppend) Finish()            { m.file.Sync() }
    84  
    85  type MutexFile struct {
    86  	mu   sync.Mutex
    87  	file *os.File
    88  }
    89  
    90  func NewMutexFile(f *os.File) *MutexFile {
    91  	m := &MutexFile{}
    92  	m.file = f
    93  	return m
    94  }
    95  
    96  func (m *MutexFile) WriteByte(b byte) {
    97  	m.mu.Lock()
    98  	m.file.Write([]byte{b})
    99  	m.file.Sync()
   100  	m.mu.Unlock()
   101  }
   102  
   103  type ChanFile struct {
   104  	req  chan request
   105  	file *os.File
   106  }
   107  
   108  type request struct {
   109  	v    byte
   110  	done chan struct{}
   111  }
   112  
   113  func NewChanFile(f *os.File) *ChanFile {
   114  	m := &ChanFile{}
   115  	m.req = make(chan request, 100)
   116  	m.file = f
   117  	return m
   118  }
   119  
   120  func (m *ChanFile) WriteByte(b byte) {
   121  	r := request{b, make(chan struct{}, 0)}
   122  	m.req <- r
   123  	<-r.done
   124  }
   125  
   126  func (m *ChanFile) Start() {
   127  	go func() {
   128  		var requests = []request{}
   129  		for {
   130  			requests = requests[:0]
   131  
   132  			r, ok := <-m.req
   133  			if !ok {
   134  				return
   135  			}
   136  
   137  			requests = append(requests, r)
   138  			m.file.Write([]byte{r.v})
   139  
   140  		combining:
   141  			for count := 1; count < 100; count++ {
   142  				select {
   143  				case r, ok := <-m.req:
   144  					if !ok {
   145  						break combining
   146  					}
   147  
   148  					requests = append(requests, r)
   149  					m.file.Write([]byte{r.v})
   150  				default:
   151  					break combining
   152  				}
   153  			}
   154  
   155  			m.file.Sync()
   156  			for _, req := range requests {
   157  				close(req.done)
   158  			}
   159  		}
   160  	}()
   161  }
   162  
   163  func (m *ChanFile) Stop() { close(m.req) }