storj.io/minio@v0.0.0-20230509071714-0cbc90f649b1/cmd/benchmark-utils_test.go (about)

     1  /*
     2   * MinIO Cloud Storage, (C) 2016, 2017 MinIO, Inc.
     3   *
     4   * Licensed under the Apache License, Version 2.0 (the "License");
     5   * you may not use this file except in compliance with the License.
     6   * You may obtain a copy of the License at
     7   *
     8   *     http://www.apache.org/licenses/LICENSE-2.0
     9   *
    10   * Unless required by applicable law or agreed to in writing, software
    11   * distributed under the License is distributed on an "AS IS" BASIS,
    12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13   * See the License for the specific language governing permissions and
    14   * limitations under the License.
    15   */
    16  
    17  package cmd
    18  
    19  import (
    20  	"bytes"
    21  	"context"
    22  	"math"
    23  	"math/rand"
    24  	"strconv"
    25  	"testing"
    26  
    27  	humanize "github.com/dustin/go-humanize"
    28  )
    29  
    30  // Benchmark utility functions for ObjectLayer.PutObject().
    31  // Creates Object layer setup ( MakeBucket ) and then runs the PutObject benchmark.
    32  func runPutObjectBenchmark(b *testing.B, obj ObjectLayer, objSize int) {
    33  	var err error
    34  	// obtains random bucket name.
    35  	bucket := getRandomBucketName()
    36  	// create bucket.
    37  	err = obj.MakeBucketWithLocation(context.Background(), bucket, BucketOptions{})
    38  	if err != nil {
    39  		b.Fatal(err)
    40  	}
    41  
    42  	// get text data generated for number of bytes equal to object size.
    43  	textData := generateBytesData(objSize)
    44  	// generate md5sum for the generated data.
    45  	// md5sum of the data to written is required as input for PutObject.
    46  
    47  	md5hex := getMD5Hash(textData)
    48  	sha256hex := ""
    49  
    50  	// benchmark utility which helps obtain number of allocations and bytes allocated per ops.
    51  	b.ReportAllocs()
    52  	// the actual benchmark for PutObject starts here. Reset the benchmark timer.
    53  	b.ResetTimer()
    54  	for i := 0; i < b.N; i++ {
    55  		// insert the object.
    56  		objInfo, err := obj.PutObject(context.Background(), bucket, "object"+strconv.Itoa(i),
    57  			mustGetPutObjReader(b, bytes.NewReader(textData), int64(len(textData)), md5hex, sha256hex), ObjectOptions{})
    58  		if err != nil {
    59  			b.Fatal(err)
    60  		}
    61  		if objInfo.ETag != md5hex {
    62  			b.Fatalf("Write no: %d: Md5Sum mismatch during object write into the bucket: Expected %s, got %s", i+1, objInfo.ETag, md5hex)
    63  		}
    64  	}
    65  	// Benchmark ends here. Stop timer.
    66  	b.StopTimer()
    67  }
    68  
    69  // Benchmark utility functions for ObjectLayer.PutObjectPart().
    70  // Creates Object layer setup ( MakeBucket ) and then runs the PutObjectPart benchmark.
    71  func runPutObjectPartBenchmark(b *testing.B, obj ObjectLayer, partSize int) {
    72  	var err error
    73  	// obtains random bucket name.
    74  	bucket := getRandomBucketName()
    75  	object := getRandomObjectName()
    76  
    77  	// create bucket.
    78  	err = obj.MakeBucketWithLocation(context.Background(), bucket, BucketOptions{})
    79  	if err != nil {
    80  		b.Fatal(err)
    81  	}
    82  
    83  	objSize := 128 * humanize.MiByte
    84  
    85  	// PutObjectPart returns etag of the object inserted.
    86  	// etag variable is assigned with that value.
    87  	var etag, uploadID string
    88  	// get text data generated for number of bytes equal to object size.
    89  	textData := generateBytesData(objSize)
    90  	// generate md5sum for the generated data.
    91  	// md5sum of the data to written is required as input for NewMultipartUpload.
    92  	uploadID, err = obj.NewMultipartUpload(context.Background(), bucket, object, ObjectOptions{})
    93  	if err != nil {
    94  		b.Fatal(err)
    95  	}
    96  
    97  	sha256hex := ""
    98  
    99  	var textPartData []byte
   100  	// benchmark utility which helps obtain number of allocations and bytes allocated per ops.
   101  	b.ReportAllocs()
   102  	// the actual benchmark for PutObjectPart starts here. Reset the benchmark timer.
   103  	b.ResetTimer()
   104  	for i := 0; i < b.N; i++ {
   105  		// insert the object.
   106  		totalPartsNR := int(math.Ceil(float64(objSize) / float64(partSize)))
   107  		for j := 0; j < totalPartsNR; j++ {
   108  			if j < totalPartsNR-1 {
   109  				textPartData = textData[j*partSize : (j+1)*partSize-1]
   110  			} else {
   111  				textPartData = textData[j*partSize:]
   112  			}
   113  			md5hex := getMD5Hash([]byte(textPartData))
   114  			var partInfo PartInfo
   115  			partInfo, err = obj.PutObjectPart(context.Background(), bucket, object, uploadID, j,
   116  				mustGetPutObjReader(b, bytes.NewReader(textPartData), int64(len(textPartData)), md5hex, sha256hex), ObjectOptions{})
   117  			if err != nil {
   118  				b.Fatal(err)
   119  			}
   120  			if partInfo.ETag != md5hex {
   121  				b.Fatalf("Write no: %d: Md5Sum mismatch during object write into the bucket: Expected %s, got %s", i+1, etag, md5hex)
   122  			}
   123  		}
   124  	}
   125  	// Benchmark ends here. Stop timer.
   126  	b.StopTimer()
   127  }
   128  
   129  // creates Erasure/FS backend setup, obtains the object layer and calls the runPutObjectPartBenchmark function.
   130  func benchmarkPutObjectPart(b *testing.B, instanceType string, objSize int) {
   131  	// create a temp Erasure/FS backend.
   132  	ctx, cancel := context.WithCancel(context.Background())
   133  	defer cancel()
   134  	objLayer, disks, err := prepareTestBackend(ctx, instanceType)
   135  	if err != nil {
   136  		b.Fatalf("Failed obtaining Temp Backend: <ERROR> %s", err)
   137  	}
   138  	// cleaning up the backend by removing all the directories and files created on function return.
   139  	defer removeRoots(disks)
   140  
   141  	// uses *testing.B and the object Layer to run the benchmark.
   142  	runPutObjectPartBenchmark(b, objLayer, objSize)
   143  }
   144  
   145  // creates Erasure/FS backend setup, obtains the object layer and calls the runPutObjectBenchmark function.
   146  func benchmarkPutObject(b *testing.B, instanceType string, objSize int) {
   147  	// create a temp Erasure/FS backend.
   148  	ctx, cancel := context.WithCancel(context.Background())
   149  	defer cancel()
   150  	objLayer, disks, err := prepareTestBackend(ctx, instanceType)
   151  	if err != nil {
   152  		b.Fatalf("Failed obtaining Temp Backend: <ERROR> %s", err)
   153  	}
   154  	// cleaning up the backend by removing all the directories and files created on function return.
   155  	defer removeRoots(disks)
   156  
   157  	// uses *testing.B and the object Layer to run the benchmark.
   158  	runPutObjectBenchmark(b, objLayer, objSize)
   159  }
   160  
   161  // creates Erasure/FS backend setup, obtains the object layer and runs parallel benchmark for put object.
   162  func benchmarkPutObjectParallel(b *testing.B, instanceType string, objSize int) {
   163  	// create a temp Erasure/FS backend.
   164  	ctx, cancel := context.WithCancel(context.Background())
   165  	defer cancel()
   166  	objLayer, disks, err := prepareTestBackend(ctx, instanceType)
   167  	if err != nil {
   168  		b.Fatalf("Failed obtaining Temp Backend: <ERROR> %s", err)
   169  	}
   170  	// cleaning up the backend by removing all the directories and files created on function return.
   171  	defer removeRoots(disks)
   172  
   173  	// uses *testing.B and the object Layer to run the benchmark.
   174  	runPutObjectBenchmarkParallel(b, objLayer, objSize)
   175  }
   176  
   177  // randomly picks a character and returns its equivalent byte array.
   178  func getRandomByte() []byte {
   179  	const letterBytes = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
   180  	// seeding the random number generator.
   181  	rand.Seed(UTCNow().UnixNano())
   182  	// pick a character randomly.
   183  	return []byte{letterBytes[rand.Intn(len(letterBytes))]}
   184  }
   185  
   186  // picks a random byte and repeats it to size bytes.
   187  func generateBytesData(size int) []byte {
   188  	// repeat the random character chosen size
   189  	return bytes.Repeat(getRandomByte(), size)
   190  }
   191  
   192  // Parallel benchmark utility functions for ObjectLayer.PutObject().
   193  // Creates Object layer setup ( MakeBucket ) and then runs the PutObject benchmark.
   194  func runPutObjectBenchmarkParallel(b *testing.B, obj ObjectLayer, objSize int) {
   195  	// obtains random bucket name.
   196  	bucket := getRandomBucketName()
   197  	// create bucket.
   198  	err := obj.MakeBucketWithLocation(context.Background(), bucket, BucketOptions{})
   199  	if err != nil {
   200  		b.Fatal(err)
   201  	}
   202  
   203  	// get text data generated for number of bytes equal to object size.
   204  	textData := generateBytesData(objSize)
   205  	// generate md5sum for the generated data.
   206  	// md5sum of the data to written is required as input for PutObject.
   207  
   208  	md5hex := getMD5Hash([]byte(textData))
   209  	sha256hex := ""
   210  
   211  	// benchmark utility which helps obtain number of allocations and bytes allocated per ops.
   212  	b.ReportAllocs()
   213  	// the actual benchmark for PutObject starts here. Reset the benchmark timer.
   214  	b.ResetTimer()
   215  
   216  	b.RunParallel(func(pb *testing.PB) {
   217  		i := 0
   218  		for pb.Next() {
   219  			// insert the object.
   220  			objInfo, err := obj.PutObject(context.Background(), bucket, "object"+strconv.Itoa(i),
   221  				mustGetPutObjReader(b, bytes.NewReader(textData), int64(len(textData)), md5hex, sha256hex), ObjectOptions{})
   222  			if err != nil {
   223  				b.Fatal(err)
   224  			}
   225  			if objInfo.ETag != md5hex {
   226  				b.Fatalf("Write no: Md5Sum mismatch during object write into the bucket: Expected %s, got %s", objInfo.ETag, md5hex)
   227  			}
   228  			i++
   229  		}
   230  	})
   231  
   232  	// Benchmark ends here. Stop timer.
   233  	b.StopTimer()
   234  }