github.com/hxx258456/ccgo@v0.0.5-0.20230213014102-48b35f46f66f/grpc/internal/profiling/buffer/buffer_test.go (about)

     1  /*
     2   *
     3   * Copyright 2019 gRPC authors.
     4   *
     5   * Licensed under the Apache License, Version 2.0 (the "License");
     6   * you may not use this file except in compliance with the License.
     7   * You may obtain a copy of the License at
     8   *
     9   *     http://www.apache.org/licenses/LICENSE-2.0
    10   *
    11   * Unless required by applicable law or agreed to in writing, software
    12   * distributed under the License is distributed on an "AS IS" BASIS,
    13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    14   * See the License for the specific language governing permissions and
    15   * limitations under the License.
    16   *
    17   */
    18  
    19  package buffer
    20  
    21  import (
    22  	"fmt"
    23  	"sync"
    24  	"testing"
    25  	"time"
    26  
    27  	"github.com/hxx258456/ccgo/grpc/internal/grpctest"
    28  )
    29  
    30  type s struct {
    31  	grpctest.Tester
    32  }
    33  
    34  func Test(t *testing.T) {
    35  	grpctest.RunSubTests(t, s{})
    36  }
    37  
    38  func (s) TestCircularBufferSerial(t *testing.T) {
    39  	var size, i uint32
    40  	var result []interface{}
    41  
    42  	size = 1 << 15
    43  	cb, err := NewCircularBuffer(size)
    44  	if err != nil {
    45  		t.Fatalf("error allocating CircularBuffer: %v", err)
    46  	}
    47  
    48  	for i = 0; i < size/2; i++ {
    49  		cb.Push(i)
    50  	}
    51  
    52  	result = cb.Drain()
    53  	if uint32(len(result)) != size/2 {
    54  		t.Fatalf("len(result) = %d; want %d", len(result), size/2)
    55  	}
    56  
    57  	// The returned result isn't necessarily sorted.
    58  	seen := make(map[uint32]bool)
    59  	for _, r := range result {
    60  		seen[r.(uint32)] = true
    61  	}
    62  
    63  	for i = 0; i < uint32(len(result)); i++ {
    64  		if !seen[i] {
    65  			t.Fatalf("seen[%d] = false; want true", i)
    66  		}
    67  	}
    68  
    69  	for i = 0; i < size; i++ {
    70  		cb.Push(i)
    71  	}
    72  
    73  	result = cb.Drain()
    74  	if uint32(len(result)) != size {
    75  		t.Fatalf("len(result) = %d; want %d", len(result), size/2)
    76  	}
    77  }
    78  
    79  func (s) TestCircularBufferOverflow(t *testing.T) {
    80  	var size, i uint32
    81  	var result []interface{}
    82  
    83  	size = 1 << 10
    84  	cb, err := NewCircularBuffer(size)
    85  	if err != nil {
    86  		t.Fatalf("error allocating CircularBuffer: %v", err)
    87  	}
    88  
    89  	for i = 0; i < 10*size; i++ {
    90  		cb.Push(i)
    91  	}
    92  
    93  	result = cb.Drain()
    94  
    95  	if uint32(len(result)) != size {
    96  		t.Fatalf("len(result) = %d; want %d", len(result), size)
    97  	}
    98  
    99  	for idx, x := range result {
   100  		if x.(uint32) < size {
   101  			t.Fatalf("result[%d] = %d; want it to be >= %d", idx, x, size)
   102  		}
   103  	}
   104  }
   105  
   106  func (s) TestCircularBufferConcurrent(t *testing.T) {
   107  	for tn := 0; tn < 2; tn++ {
   108  		var size uint32
   109  		var result []interface{}
   110  
   111  		size = 1 << 6
   112  		cb, err := NewCircularBuffer(size)
   113  		if err != nil {
   114  			t.Fatalf("error allocating CircularBuffer: %v", err)
   115  		}
   116  
   117  		type item struct {
   118  			R uint32
   119  			N uint32
   120  			T time.Time
   121  		}
   122  
   123  		var wg sync.WaitGroup
   124  		for r := uint32(0); r < 1024; r++ {
   125  			wg.Add(1)
   126  			go func(r uint32) {
   127  				for n := uint32(0); n < size; n++ {
   128  					cb.Push(item{R: r, N: n, T: time.Now()})
   129  				}
   130  				wg.Done()
   131  			}(r)
   132  		}
   133  
   134  		// Wait for all goroutines to finish only in one test. Draining
   135  		// concurrently while Pushes are still happening will test for races in the
   136  		// Draining lock.
   137  		if tn == 0 {
   138  			wg.Wait()
   139  		}
   140  
   141  		result = cb.Drain()
   142  
   143  		// Can't expect the buffer to be full if the Pushes aren't necessarily done.
   144  		if tn == 0 {
   145  			if uint32(len(result)) != size {
   146  				t.Fatalf("len(result) = %d; want %d", len(result), size)
   147  			}
   148  		}
   149  
   150  		// There can be absolutely no expectation on the order of the data returned
   151  		// by Drain because: (a) everything is happening concurrently (b) a
   152  		// round-robin is used to write to different queues (and therefore
   153  		// different cachelines) for less write contention.
   154  
   155  		// Wait for all goroutines to complete before moving on to other tests. If
   156  		// the benchmarks run after this, it might affect performance unfairly.
   157  		wg.Wait()
   158  	}
   159  }
   160  
   161  func BenchmarkCircularBuffer(b *testing.B) {
   162  	x := 1
   163  	for size := 1 << 16; size <= 1<<20; size <<= 1 {
   164  		for routines := 1; routines <= 1<<8; routines <<= 1 {
   165  			b.Run(fmt.Sprintf("goroutines:%d/size:%d", routines, size), func(b *testing.B) {
   166  				cb, err := NewCircularBuffer(uint32(size))
   167  				if err != nil {
   168  					b.Fatalf("error allocating CircularBuffer: %v", err)
   169  				}
   170  
   171  				perRoutine := b.N / routines
   172  				var wg sync.WaitGroup
   173  				for r := 0; r < routines; r++ {
   174  					wg.Add(1)
   175  					go func() {
   176  						for i := 0; i < perRoutine; i++ {
   177  							cb.Push(&x)
   178  						}
   179  						wg.Done()
   180  					}()
   181  				}
   182  				wg.Wait()
   183  			})
   184  		}
   185  	}
   186  }