From 3d02f7471e686a7cc39a8abf27e1437e89632bc3 Mon Sep 17 00:00:00 2001 From: karthic rao Date: Sun, 26 Jun 2016 08:52:04 +0530 Subject: [PATCH] Benchmarks for various object sizes for FS/XL GetObject (#1984) --- benchmark-utils_test.go | 111 ++++++++++++++++++++ object-api-getobjectinfo_test.go | 168 ++++++++++++++++++++++--------- test-utils_test.go | 23 +++-- 3 files changed, 245 insertions(+), 57 deletions(-) create mode 100644 benchmark-utils_test.go diff --git a/benchmark-utils_test.go b/benchmark-utils_test.go new file mode 100644 index 000000000..6397f0f01 --- /dev/null +++ b/benchmark-utils_test.go @@ -0,0 +1,111 @@ +/* + * Minio Cloud Storage, (C) 2016 Minio, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package main + +import ( + "bytes" + "crypto/md5" + "encoding/hex" + "math/rand" + "strconv" + "testing" + "time" +) + +// Creates Object layer setup ( MakeBucket, PutObject) and then runs the benchmark. +func runGetObjectBenchmark(b *testing.B, obj ObjectLayer, objSize int) { + var err error + // obtains random bucket name. + bucket := getRandomBucketName() + // create bucket. + err = obj.MakeBucket(bucket) + if err != nil { + b.Fatal(err) + } + + // PutObject returns md5Sum of the object inserted. + // md5Sum variable is assigned with that value. + var md5Sum string + for i := 0; i < 10; i++ { + // get text data generated for number of bytes equal to object size. + textData := generateBytesData(objSize) + // generate md5sum for the generated data. + // md5sum of the data to written is required as input for PutObject. + // PutObject is the functions which writes the data onto the FS/XL backend. + hasher := md5.New() + hasher.Write([]byte(textData)) + metadata := make(map[string]string) + metadata["md5Sum"] = hex.EncodeToString(hasher.Sum(nil)) + // insert the object. + md5Sum, err = obj.PutObject(bucket, "object"+strconv.Itoa(i), int64(len(textData)), bytes.NewBuffer(textData), metadata) + if err != nil { + b.Fatal(err) + } + if md5Sum != metadata["md5Sum"] { + b.Fatalf("Write no: %d: Md5Sum mismatch during object write into the bucket: Expected %s, got %s", i+1, md5Sum, metadata["md5Sum"]) + } + } + + // benchmark utility which helps obtain number of allocations and bytes allocated per ops. + b.ReportAllocs() + // the actual benchmark for GetObject starts here. Reset the benchmark timer. + b.ResetTimer() + for i := 0; i < b.N; i++ { + var buffer = new(bytes.Buffer) + err = obj.GetObject(bucket, "object"+strconv.Itoa(i%10), 0, int64(objSize), buffer) + if err != nil { + b.Error(err) + } + } + // Benchmark ends here. Stop timer. + b.StopTimer() + +} +func generateBytesData(size int) []byte { + // randomly picks a character and returns its equivalent byte array. + getRandomByte := func() []byte { + const letterBytes = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" + // seeding the random number generator. + rand.Seed(time.Now().UnixNano()) + var b byte + // pick a character randomly. + b = letterBytes[rand.Intn(len(letterBytes))] + return []byte{b} + } + // repeat the random character choosen size + return bytes.Repeat(getRandomByte(), size) +} + +// creates XL/FS backend setup, obtains the object layer and calls the runGetObjectBenchmark function. +func benchmarkGetObject(b *testing.B, instanceType string, runBenchMark func(b *testing.B, obj ObjectLayer)) { + // create a temp XL/FS backend. + objLayer, disks, err := makeTestBackend(instanceType) + if err != nil { + b.Fatalf("Failed obtaining Temp Backend: %s", err) + } + // cleaning up the backend by removing all the directories and files created. + defer removeRoots(disks) + // calling runGetObjectBenchmark which uses *testing.B and the object Layer to run the benchmark. + runBenchMark(b, objLayer) +} + +// closure for returning the get object benchmark executor for given object size in bytes. +func returnGetObjectBenchmark(objSize int) func(*testing.B, ObjectLayer) { + return func(b *testing.B, obj ObjectLayer) { + runGetObjectBenchmark(b, obj, objSize) + } +} diff --git a/object-api-getobjectinfo_test.go b/object-api-getobjectinfo_test.go index 5c9f06daa..de54bb1db 100644 --- a/object-api-getobjectinfo_test.go +++ b/object-api-getobjectinfo_test.go @@ -18,10 +18,6 @@ package main import ( "bytes" - "crypto/md5" - "encoding/hex" - "io/ioutil" - "strconv" "testing" ) @@ -109,47 +105,127 @@ func testGetObjectInfo(obj ObjectLayer, instanceType string, t *testing.T) { } } -func BenchmarkGetObjectFS(b *testing.B) { - // Make a temporary directory to use as the obj. - directory, err := ioutil.TempDir("", "minio-benchmark-getobject") - if err != nil { - b.Fatal(err) - } - defer removeAll(directory) +// Benchmarks for ObjectLayer.GetObject(). +// The intent is to benchamrk GetObject for various sizes ranging from few bytes to 100MB. +// Also each of these Benchmarks are run both XL and FS backends. - // Create the obj. - obj, err := newFSObjects(directory) - if err != nil { - b.Fatal(err) - } - - // Make a bucket and put in a few objects. - err = obj.MakeBucket("bucket") - if err != nil { - b.Fatal(err) - } - - text := "Jack and Jill went up the hill / To fetch a pail of water." - hasher := md5.New() - hasher.Write([]byte(text)) - metadata := make(map[string]string) - for i := 0; i < 10; i++ { - metadata["md5Sum"] = hex.EncodeToString(hasher.Sum(nil)) - _, err = obj.PutObject("bucket", "object"+strconv.Itoa(i), int64(len(text)), bytes.NewBufferString(text), metadata) - if err != nil { - b.Fatal(err) - } - } - - b.ResetTimer() - for i := 0; i < b.N; i++ { - var buffer = new(bytes.Buffer) - err = obj.GetObject("bucket", "object"+strconv.Itoa(i%10), 0, int64(len([]byte(text))), buffer) - if err != nil { - b.Error(err) - } - if buffer.Len() != len(text) { - b.Errorf("GetObject returned incorrect length %d (should be %d)\n", buffer.Len(), len(text)) - } - } +// BenchmarkGetObjectVerySmallFS - Benchmark FS.GetObject() for object size of 10 bytes. +func BenchmarkGetObjectVerySmallFS(b *testing.B) { + benchmarkGetObject(b, "FS", returnGetObjectBenchmark(10)) +} + +// BenchmarkGetObjectVerySmallXL - Benchmark XL.GetObject() for object size of 10 bytes. +func BenchmarkGetObjectVerySmallXL(b *testing.B) { + benchmarkGetObject(b, "XL", returnGetObjectBenchmark(10)) +} + +// BenchmarkGetObject10KbFS - Benchmark FS.GetObject() for object size of 10KB. +func BenchmarkGetObject10KbFS(b *testing.B) { + benchmarkGetObject(b, "FS", returnGetObjectBenchmark(10*1024)) +} + +// BenchmarkGetObject10KbXL - Benchmark XL.GetObject() for object size of 10KB. +func BenchmarkGetObject10KbXL(b *testing.B) { + benchmarkGetObject(b, "XL", returnGetObjectBenchmark(10*1024)) +} + +// BenchmarkGetObject100KbFS - Benchmark FS.GetObject() for object size of 100KB. +func BenchmarkGetObject100KbFS(b *testing.B) { + benchmarkGetObject(b, "FS", returnGetObjectBenchmark(100*1024)) +} + +// BenchmarkGetObject100KbXL - Benchmark XL.GetObject() for object size of 100KB. +func BenchmarkGetObject100KbXL(b *testing.B) { + benchmarkGetObject(b, "XL", returnGetObjectBenchmark(100*1024)) +} + +// BenchmarkGetObject1MbFS - Benchmark FS.GetObject() for object size of 1MB. +func BenchmarkGetObject1MbFS(b *testing.B) { + benchmarkGetObject(b, "FS", returnGetObjectBenchmark(1024*1024)) +} + +// BenchmarkGetObject1MbXL - Benchmark XL.GetObject() for object size of 1MB. +func BenchmarkGetObject1MbXL(b *testing.B) { + benchmarkGetObject(b, "XL", returnGetObjectBenchmark(1024*1024)) +} + +// BenchmarkGetObject5MbFS - Benchmark FS.GetObject() for object size of 5MB. +func BenchmarkGetObject5MbFS(b *testing.B) { + benchmarkGetObject(b, "FS", returnGetObjectBenchmark(5*1024*1024)) +} + +// BenchmarkGetObject5MbXL - Benchmark XL.GetObject() for object size of 5MB. +func BenchmarkGetObject5MbXL(b *testing.B) { + benchmarkGetObject(b, "XL", returnGetObjectBenchmark(5*1024*1024)) +} + +// BenchmarkGetObject10MbFS - Benchmark FS.GetObject() for object size of 10MB. +func BenchmarkGetObject10MbFS(b *testing.B) { + benchmarkGetObject(b, "FS", returnGetObjectBenchmark(10*1024*1024)) +} + +// BenchmarkGetObject10MbXL - Benchmark XL.GetObject() for object size of 10MB. +func BenchmarkGetObject10MbXL(b *testing.B) { + benchmarkGetObject(b, "XL", returnGetObjectBenchmark(10*1024*1024)) +} + +// BenchmarkGetObject25MbFS - Benchmark FS.GetObject() for object size of 25MB. +func BenchmarkGetObject25MbFS(b *testing.B) { + benchmarkGetObject(b, "FS", returnGetObjectBenchmark(25*1024*1024)) + +} + +// BenchmarkGetObject25MbXL - Benchmark XL.GetObject() for object size of 25MB. +func BenchmarkGetObject25MbXL(b *testing.B) { + benchmarkGetObject(b, "XL", returnGetObjectBenchmark(25*1024*1024)) +} + +// BenchmarkGetObject50MbFS - Benchmark FS.GetObject() for object size of 50MB. +func BenchmarkGetObject50MbFS(b *testing.B) { + benchmarkGetObject(b, "FS", returnGetObjectBenchmark(50*1024*1024)) +} + +// BenchmarkGetObject50MbXL - Benchmark XL.GetObject() for object size of 50MB. +func BenchmarkGetObject50MbXL(b *testing.B) { + benchmarkGetObject(b, "XL", returnGetObjectBenchmark(50*1024*1024)) +} + +// BenchmarkGetObject100MbFS - Benchmark FS.GetObject() for object size of 100MB. +func BenchmarkGetObject100MbFS(b *testing.B) { + benchmarkGetObject(b, "FS", returnGetObjectBenchmark(100*1024*1024)) +} + +// BenchmarkGetObject100MbXL - Benchmark XL.GetObject() for object size of 100MB. +func BenchmarkGetObject100MbXL(b *testing.B) { + benchmarkGetObject(b, "XL", returnGetObjectBenchmark(100*1024*1024)) +} + +// BenchmarkGetObject200MbFS - Benchmark FS.GetObject() for object size of 200MB. +func BenchmarkGetObject200MbFS(b *testing.B) { + benchmarkGetObject(b, "FS", returnGetObjectBenchmark(200*1024*1024)) +} + +// BenchmarkGetObject200MbXL - Benchmark XL.GetObject() for object size of 200MB. +func BenchmarkGetObject200MbXL(b *testing.B) { + benchmarkGetObject(b, "XL", returnGetObjectBenchmark(200*1024*1024)) +} + +// BenchmarkGetObject500MbFS - Benchmark FS.GetObject() for object size of 500MB. +func BenchmarkGetObject500MbFS(b *testing.B) { + benchmarkGetObject(b, "FS", returnGetObjectBenchmark(500*1024*1024)) +} + +// BenchmarkGetObject500MbXL - Benchmark XL.GetObject() for object size of 500MB. +func BenchmarkGetObject500MbXL(b *testing.B) { + benchmarkGetObject(b, "XL", returnGetObjectBenchmark(500*1024*1024)) +} + +// BenchmarkGetObject1GbFS - Benchmark FS.GetObject() for object size of 1GB. +func BenchmarkGetObject1GbFS(b *testing.B) { + benchmarkGetObject(b, "FS", returnGetObjectBenchmark(1024*1024*1024)) +} + +// BenchmarkGetObjectGbXL - Benchmark XL.GetObject() for object size of 1GB. +func BenchmarkGetObject1GbXL(b *testing.B) { + benchmarkGetObject(b, "XL", returnGetObjectBenchmark(1024*1024*1024)) } diff --git a/test-utils_test.go b/test-utils_test.go index b1cf589e8..030ba6d85 100644 --- a/test-utils_test.go +++ b/test-utils_test.go @@ -78,15 +78,16 @@ func StartTestServer(t TestErrHandler, instanceType string) TestServer { // create an instance of TestServer. testServer := TestServer{} // create temporary backend for the test server. - erasureDisks, err := makeTestBackend(instanceType) + _, erasureDisks, err := makeTestBackend(instanceType) + if err != nil { - t.Fatalf("Failed obtaining Temp XL layer: %s", err) + t.Fatalf("Failed obtaining Temp Backend: %s", err) } testServer.Disks = erasureDisks // Obtain temp root. root, err := getTestRoot() if err != nil { - t.Fatalf("Failed obtaining Temp XL layer: %s", err) + t.Fatalf("Failed obtaining Temp Root for the backend Backend: %s", err) } testServer.Root = root testServer.Disks = erasureDisks @@ -251,24 +252,24 @@ func newTestRequest(method, urlStr string, contentLength int64, body io.ReadSeek // if the option is // FS: Returns a temp single disk setup initializes FS Backend. // XL: Returns a 16 temp single disk setup and initializse XL Backend. -func makeTestBackend(instanceType string) ([]string, error) { +func makeTestBackend(instanceType string) (ObjectLayer, []string, error) { switch instanceType { case "FS": - _, fsroot, err := getSingleNodeObjectLayer() + objLayer, fsroot, err := getSingleNodeObjectLayer() if err != nil { - return []string{}, err + return nil, []string{}, err } - return []string{fsroot}, err + return objLayer, []string{fsroot}, err case "XL": - _, erasureDisks, err := getXLObjectLayer() + objectLayer, erasureDisks, err := getXLObjectLayer() if err != nil { - return []string{}, err + return nil, []string{}, err } - return erasureDisks, err + return objectLayer, erasureDisks, err default: errMsg := "Invalid instance type, Only FS and XL are valid options" - return []string{}, fmt.Errorf("Failed obtaining Temp XL layer: %s", errMsg) + return nil, []string{}, fmt.Errorf("Failed obtaining Temp XL layer: %s", errMsg) } }