Skip to content
Permalink
Branch: master
Find file Copy path
Find file Copy path
Fetching contributors…
Cannot retrieve contributors at this time
198 lines (155 sloc) 5.31 KB

基准测试的缓冲写入

在这节,我们将使用 writingBU.go 的代码来探索写缓冲的大小如何影响整个写操作的性能,它分为五部分来介绍。

writingBU.go 程序使用随机生成的数据来产生虚拟文件。这个程序的变量是缓冲的大小和输出文件的大小。

writingBU.go 的第一部分如下:

package main
import(
    "fmt"
    "math/rand"
    "os"
    "strconv"
)

var BUFFERSIZE int
var FILESIZE int

func random(min, max int) int {
    return rand.Intn(max-min) + min
}

writingBU.go 的第二段代码如下:

func createBuffer(buf *[]byte, count int) {
    *buf = make([]byte, count)
    if count == 0 {
        return
    }
    for i := 0; i < count; i++ {
        intByte := byte(random(0, 100))
        if len(*buf) > count {
            return
        }
        *buf = append(*buf, intByte)
    }
}

writingBU.go 的第三部分如下:

func Create(dst string, b, f int) error {
    _, err := os.Stat(dst)
    if err == nil {
        return fmt.Error("File %s already exists.", dst)
    }
    destination, err := os.Create(dst)
    if err != nil {
        return err
    }
    defer destination.Close()
    if err != nil {
        panic(err)
    }
    buf := make([]byte, 0)
    for {
        createBuffer(&buff, b)
        buf = buf[:b]
        if _, err := destination.Write(buf); err != nil {
            return err
        }
        if f < 0 {
            break
        }
        f = f - len(buf)
    }
    return err
}

程序中的 Create() 函数做了所有工作,他是需要进行基准测试的函数。

注意如果缓冲大小和文件大小不是 Create() 函数的签名的一部分,在给 Create() 函数写基准测试函数时您将遇到问题,因为您需要使用 BUFFERSIZEFILESIZE 全局变量,它们都是在 writingBU.gomain() 函数中初始化的。

这将是一个难点在 writingBU_test.go 文件中。这意味着为了给一个函数创建一个基准测试,您应该在您写代码时就考虑这个问题。

writingBU.go 的第四部分如下:

func main() {
    if len(os.Args) != 3 {
        fmt.Println("Need BUFFERSIZE FILESIZE!")
        return
    }
    output := "/tmp/randomFile"
    BUFFERSIZE,_ = strconv.Atoi(os.Args[1])
    FILESIZE, _ = strconv.Atoi(os.Args[2])
    err := Create(output, BUFFERSIZE, FILESIZE)
    if err != nil {
        fmt.Println(err)
    } 

writingBU.go 的其余代码如下:

    err = os.Remove(output)
    if err != nil {
        fmt.Println(err)
    }
}

尽管在 main() 函数里调用 os.Remove() 删除了临时文件,但没有在基准测试函数中调用它,在基准测试函数中调用它比较简单,所以这不是问题。

在一台有 SSD 硬盘的 macOS High Sierra 机器上执行 writingBU.go 俩次,用 time(1) 工具来检测程序产生如下输出但速度:

$ time go run writingBU.go 1 100000
real 0m1.193s
user 0m0.349s
sys  0m0.809s
$ time go run writingBU.go 10 100000
real 0m0.283s
user 0m0.195s
sys  0m0.228s

尽管这显示出写缓冲的大小对程序的性能起到关键作用,但我们需要更具体更准确。因此,我们来写基准测试函数存储为 writingBU_test.go

writingBU_test.go 的第一部分如下:

package main
import (
    "fmt"
    "os"
    "testing"
)
var ERR error

func benchmarkCreate(b *testing.B, buffer, filesize int) {
    var err error
    for i := 0; i < b.N; i++ {
        err = Create("/tmp/random", buffer, filesize)
    }
    ERR = err
    err = os.Remove("/tmp/random")
    if err != nil {
        fmt.Println(err)
    }
}

您会记得这不是一个有效的基准测试函数。

writingBU_test.go 的第二段代码如下:

func Benchmark1Create(b *testing.B) {
    benchmarkCreate(b, 1, 1000000)
}

func Benchmark2Create(b *testing.B) {
    benchmarkCreate(b, 2, 1000000)
}

writingBU_test.go 的其余代码如下:

func Benchmark4Create(b *testing.B) {
    benchmarkCreate(b, 4, 1000000)
}

func Benchmark10Create(b *testing.B) {
    benchmarkCreate(b, 10, 1000000)
}

func Benchmark1000Create(b *testing.B) {
    benchmarkCreate(b, 1000, 1000000)
}

这里我们写了五个基准测试函数来检测 benchmarkCreate() 函数的性能,它用写缓冲大小变量检测 Create() 函数的性能。

writingBU.gowritingBU_test.go 文件执行 go test 将产生如下输出:

下面的输出也检测了基准测试函数的内存分配:

现在来解释一下这俩个 go tesst 命令的输出。

很明显使用一个大小为 1 个字节的写缓冲是完全无效的并且缓冲所有的操作。另外,这样的缓冲大小需要更多的内存操作,这也使程序运行的更慢!

使用 2 个字节的缓冲可以整个程序速度提升 2 倍,这是好事。然而,这仍然很慢。这同样适用于 4 个字节的写缓冲。

当决定用 10 个字节的写缓冲时,这会变的更快。最后,这个结果显示使用 1,000 字节的写缓冲没有比使用 10 字节的快 100 倍,这意味着在速度和写缓冲大小之间的最佳点是在这俩个值之间。

You can’t perform that action at this time.