github.com/Lz-Gustavo/beemport@v0.0.0-20220203143007-a29c25c47274/conctable_bench_test.go (about)

     1  package beemport
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"math/rand"
     7  	"os"
     8  	"runtime"
     9  	"sync"
    10  	"testing"
    11  	"time"
    12  
    13  	"github.com/Lz-Gustavo/beemport/pb"
    14  )
    15  
    16  // Dear dev, avoid crash on your IDE by running with:
    17  // go test -run none -bench BenchmarkAlgosThroughput -benchtime 1ns -benchmem -v
    18  func BenchmarkConcTableThroughput(b *testing.B) {
    19  	b.SetParallelism(runtime.NumCPU())
    20  	numCommands, diffKeys, writePercent := uint64(1000000), 1000, 50
    21  	log := make(chan *pb.Entry, numCommands)
    22  
    23  	// dummy goroutine that creates a random log of commands
    24  	go createRandomLog(numCommands, diffKeys, writePercent, log)
    25  
    26  	chA := make(chan *pb.Entry)
    27  	chB := make(chan *pb.Entry)
    28  
    29  	wg := &sync.WaitGroup{}
    30  	mu := &sync.Mutex{}
    31  	wg.Add(2)
    32  
    33  	go runTraditionalLog(chA, numCommands, mu, wg)
    34  	go runConcTable(chB, numCommands, mu, wg)
    35  
    36  	// fan-out that output to the different goroutines
    37  	go splitIntoWorkers(log, chA, chB)
    38  
    39  	// close the input log channel once all algorithms are executed
    40  	wg.Wait()
    41  	close(log)
    42  }
    43  
    44  func createRandomLog(n uint64, dif, wrt int, out chan<- *pb.Entry) {
    45  	srand := rand.NewSource(time.Now().UnixNano())
    46  	r := rand.New(srand)
    47  
    48  	for i := uint64(0); i < n; i++ {
    49  		cmd := pb.Entry{
    50  			Id:  i,
    51  			Key: int64(r.Intn(dif)),
    52  		}
    53  
    54  		// WRITE operation
    55  		if cn := r.Intn(100); cn < wrt {
    56  			cmd.WriteOp = true
    57  			cmd.Command = generateRandByteSlice()
    58  		}
    59  		out <- &cmd
    60  	}
    61  
    62  	// indicates the last command in the log, forcing consumer goroutines to halt
    63  	out <- &pb.Entry{}
    64  }
    65  
    66  func splitIntoWorkers(src <-chan *pb.Entry, wrks ...chan<- *pb.Entry) {
    67  	for cmd := range src {
    68  		for _, ch := range wrks {
    69  			// avoid blocking receive on the sync ch
    70  			go func(dest chan<- *pb.Entry, c *pb.Entry) {
    71  				dest <- c
    72  			}(ch, cmd)
    73  		}
    74  	}
    75  }
    76  
    77  func runConcTable(log <-chan *pb.Entry, n uint64, mu *sync.Mutex, wg *sync.WaitGroup) {
    78  	ct := NewConcTable(context.TODO())
    79  	var i uint64
    80  	defer wg.Done()
    81  
    82  	start := time.Now()
    83  	for cmd := range log {
    84  		if i < n {
    85  			ct.Log(cmd)
    86  			i++
    87  
    88  		} else {
    89  			// finished logging
    90  			break
    91  		}
    92  	}
    93  
    94  	// elapsed time to interpret the sequence of commands and construct the tree struct
    95  	construct := time.Since(start)
    96  
    97  	var (
    98  		fn, id string
    99  		out    []*pb.Entry
   100  
   101  		// elapsed time to recovery the entire log
   102  		recov time.Duration
   103  	)
   104  
   105  	// TODO: call reduce procedure for each configuration
   106  
   107  	start = time.Now()
   108  	err := dumpLogIntoFile("./", fn, out)
   109  	if err != nil {
   110  		fmt.Println(err.Error())
   111  	}
   112  	dump := time.Since(start)
   113  
   114  	mu.Lock()
   115  	fmt.Println(
   116  		"\n====================",
   117  		"\n===", id,
   118  		"\nRemoved cmds: ", n-uint64(len(out)),
   119  		"\nConstruction Time: ", construct,
   120  		"\nCompactation Time: ", recov,
   121  		"\nInstallation Time:", dump,
   122  		"\n====================",
   123  	)
   124  	mu.Unlock()
   125  }
   126  
   127  func runTraditionalLog(log <-chan *pb.Entry, n uint64, mu *sync.Mutex, wg *sync.WaitGroup) {
   128  	logfile := make([]*pb.Entry, 0, n)
   129  	var i uint64
   130  	defer wg.Done()
   131  
   132  	start := time.Now()
   133  	for cmd := range log {
   134  		if i < n {
   135  			logfile = append(logfile, cmd)
   136  			i++
   137  
   138  		} else {
   139  			// finished logging
   140  			break
   141  		}
   142  	}
   143  
   144  	construct := time.Since(start)
   145  	fn := "traditionallog-bench.out"
   146  
   147  	start = time.Now()
   148  	err := dumpLogIntoFile("./", fn, logfile)
   149  	if err != nil {
   150  		fmt.Println(err.Error())
   151  	}
   152  	dump := time.Since(start)
   153  
   154  	mu.Lock()
   155  	fmt.Println(
   156  		"\n====================",
   157  		"\n=== Traditional Log Benchmark",
   158  		"\nRemoved cmds:", n-uint64(len(logfile)),
   159  		"\nConstruction Time:", construct,
   160  		"\nCompactation Time: -",
   161  		"\nInstallation Time:", dump,
   162  		"\n====================",
   163  	)
   164  	mu.Unlock()
   165  }
   166  
   167  func dumpLogIntoFile(folder, name string, log []*pb.Entry) error {
   168  	if _, exists := os.Stat(folder); os.IsNotExist(exists) {
   169  		os.Mkdir(folder, 0744)
   170  	}
   171  
   172  	out, err := os.OpenFile(name, os.O_CREATE|os.O_WRONLY, 0744)
   173  	if err != nil {
   174  		return err
   175  	}
   176  	defer out.Close()
   177  
   178  	for _, cmd := range log {
   179  		_, err = fmt.Fprintf(out, "%v %d %v\n", cmd.WriteOp, cmd.Key, cmd.Command)
   180  		if err != nil {
   181  			return err
   182  		}
   183  	}
   184  	return nil
   185  }