isula-build/patch/0093-perf-use-bufio-reader-instead-ioutil.ReadFile.patch
DCCooper f0037c1207 isula-build: sync upstream patches
Signed-off-by: DCCooper <1866858@gmail.com>
(cherry picked from commit 4e509c26e0bace00e01cde6a8a6eb50e418a4e77)
2021-12-09 16:04:07 +08:00

201 lines
4.8 KiB
Diff

From a1becee58561451daea4bd69989b0806cfa9ceab Mon Sep 17 00:00:00 2001
From: DCCooper <1866858@gmail.com>
Date: Tue, 16 Nov 2021 17:31:58 +0800
Subject: [PATCH 22/29] perf:use bufio reader instead ioutil.ReadFile
reason: read file with fixed chunk size instead of
the whole file into memory cause memory pressure
Signed-off-by: DCCooper <1866858@gmail.com>
---
util/cipher.go | 38 +++++++++++++++++++-----
util/cipher_test.go | 84 +++++++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 114 insertions(+), 8 deletions(-)
diff --git a/util/cipher.go b/util/cipher.go
index ecbbc47..a5e3125 100644
--- a/util/cipher.go
+++ b/util/cipher.go
@@ -14,6 +14,7 @@
package util
import (
+ "bufio"
"crypto"
"crypto/aes"
"crypto/cipher"
@@ -234,6 +235,33 @@ func ReadPublicKey(path string) (rsa.PublicKey, error) {
return *key, nil
}
+func checkSumReader(path string) (string, error) {
+ const bufferSize = 32 * 1024 // 32KB
+
+ file, err := os.Open(filepath.Clean(path))
+ if err != nil {
+ return "", errors.Wrapf(err, "hash file failed")
+ }
+ defer func() {
+ if cErr := file.Close(); cErr != nil && err == nil {
+ err = cErr
+ }
+ }()
+ buf := make([]byte, bufferSize)
+ reader := bufio.NewReader(file)
+ hasher := sha256.New()
+ for {
+ switch n, err := reader.Read(buf); err {
+ case nil:
+ hasher.Write(buf[:n])
+ case io.EOF:
+ return fmt.Sprintf("%x", hasher.Sum(nil)), nil
+ default:
+ return "", err
+ }
+ }
+}
+
func hashFile(path string) (string, error) {
cleanPath := filepath.Clean(path)
if f, err := os.Stat(cleanPath); err != nil {
@@ -242,12 +270,7 @@ func hashFile(path string) (string, error) {
return "", errors.New("failed to hash directory")
}
- file, err := ioutil.ReadFile(cleanPath) // nolint:gosec
- if err != nil {
- return "", errors.Wrapf(err, "hash file failed")
- }
-
- return fmt.Sprintf("%x", sha256.Sum256(file)), nil
+ return checkSumReader(path)
}
func hashDir(path string) (string, error) {
@@ -261,11 +284,10 @@ func hashDir(path string) (string, error) {
return nil
}
if !info.IsDir() {
- f, err := ioutil.ReadFile(cleanPath) // nolint:gosec
+ fileHash, err := hashFile(cleanPath)
if err != nil {
return err
}
- fileHash := fmt.Sprintf("%x", sha256.Sum256(f))
checkSum = fmt.Sprintf("%s%s", checkSum, fileHash)
}
return nil
diff --git a/util/cipher_test.go b/util/cipher_test.go
index bab6dfe..4bbe894 100644
--- a/util/cipher_test.go
+++ b/util/cipher_test.go
@@ -15,10 +15,13 @@ package util
import (
"crypto"
+ "crypto/rand"
"crypto/sha1"
"crypto/sha256"
"crypto/sha512"
+ "fmt"
"hash"
+ "io"
"io/ioutil"
"os"
"path/filepath"
@@ -31,6 +34,9 @@ import (
)
const (
+ sizeKB = 1024
+ sizeMB = 1024 * sizeKB
+ sizeGB = 1024 * sizeMB
maxRepeatTime = 1000000
)
@@ -453,3 +459,81 @@ func TestCheckSum(t *testing.T) {
})
}
}
+
+func createFileWithSize(path string, size int) error {
+ file, err := os.Create(path)
+ if err != nil {
+ return err
+ }
+ _, err = io.CopyN(file, rand.Reader, int64(size))
+ return err
+}
+
+func benchmarkSHA256SumWithFileSize(b *testing.B, fileSize int) {
+ b.ReportAllocs()
+ filepath := fs.NewFile(b, b.Name())
+ defer filepath.Remove()
+ _ = createFileWithSize(filepath.Path(), fileSize)
+ b.ResetTimer()
+ for n := 0; n < b.N; n++ {
+ _, _ = SHA256Sum(filepath.Path())
+ }
+}
+
+func BenchmarkSHA256Sum(b *testing.B) {
+ tests := []struct {
+ fileSuffix string
+ fileSize int
+ }{
+ {fileSuffix: "100MB", fileSize: 100 * sizeMB},
+ {fileSuffix: "200MB", fileSize: 200 * sizeMB},
+ {fileSuffix: "500MB", fileSize: 500 * sizeMB},
+ {fileSuffix: "1GB", fileSize: 1 * sizeGB},
+ {fileSuffix: "2GB", fileSize: 2 * sizeGB},
+ {fileSuffix: "4GB", fileSize: 4 * sizeGB},
+ {fileSuffix: "8GB", fileSize: 8 * sizeGB},
+ }
+
+ for _, t := range tests {
+ name := fmt.Sprintf("BenchmarkSHA256SumWithFileSize_%s", t.fileSuffix)
+ b.Run(name, func(b *testing.B) {
+ benchmarkSHA256SumWithFileSize(b, t.fileSize)
+ })
+ }
+}
+
+func TestCreateFileWithSize(t *testing.T) {
+ newFile := fs.NewFile(t, t.Name())
+ defer newFile.Remove()
+ type args struct {
+ path string
+ size int
+ }
+ tests := []struct {
+ name string
+ args args
+ wantErr bool
+ }{
+ {
+ name: "TC-generate 500MB file",
+ args: args{
+ path: newFile.Path(),
+ size: 500 * sizeMB,
+ },
+ },
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ err := createFileWithSize(tt.args.path, tt.args.size)
+ if (err != nil) != tt.wantErr {
+ t.Errorf("createFileWithSize() error = %v, wantErr %v", err, tt.wantErr)
+ }
+ if err == nil {
+ file, _ := os.Stat(tt.args.path)
+ if file.Size() != int64(tt.args.size) {
+ t.Errorf("createFileWithSize() size = %v, actually %v", tt.args.size, file.Size())
+ }
+ }
+ })
+ }
+}
--
1.8.3.1