mirror of
https://github.com/gopl-zh/gopl-zh.github.com.git
synced 2025-08-05 15:12:33 +00:00
fix typo and optimize.
Change-Id: I7b6938936231fd722814984678ffa30402539fd9
This commit is contained in:
@@ -14,7 +14,7 @@ func BenchmarkIsPalindrome(b *testing.B) {
|
||||
}
|
||||
```
|
||||
|
||||
我们用下面的命令运行基准测试。和普通测试不同的是,默认情况下不运行任何基准测试。我们需要通过`-bench`命令行标志参数手工指定要运行的基准测试函数。该参数是一个正则表达式,用于匹配要执行的基准测试函数的名字,默认值是空的。其中“.”模式将可以匹配所有基准测试函数,但是这里总共只有一个基准测试函数,因此和`-bench=IsPalindrome`参数是等价的效果。
|
||||
我们用下面的命令运行基准测试。和普通测试不同的是,默认情况下不运行任何基准测试。我们需要通过`-bench`命令行标志参数手工指定要运行的基准测试函数。该参数是一个正则表达式,用于匹配要执行的基准测试函数的名字,默认值是空的。其中“.”模式将可以匹配所有基准测试函数,但因为这里只有一个基准测试函数,因此和`-bench=IsPalindrome`参数是等价的效果。
|
||||
|
||||
```
|
||||
$ cd $GOPATH/src/gopl.io/ch11/word2
|
||||
@@ -24,13 +24,13 @@ BenchmarkIsPalindrome-8 1000000 1035 ns/op
|
||||
ok gopl.io/ch11/word2 2.179s
|
||||
```
|
||||
|
||||
结果中基准测试名的数字后缀部分,这里是8,表示运行时对应的GOMAXPROCS的值,这对于一些和并发相关的基准测试是重要的信息。
|
||||
结果中基准测试名的数字后缀部分,这里是8,表示运行时对应的GOMAXPROCS的值,这对于一些与并发相关的基准测试是重要的信息。
|
||||
|
||||
报告显示每次调用IsPalindrome函数花费1.035微秒,是执行1,000,000次的平均时间。因为基准测试驱动器开始时并不知道每个基准测试函数运行所花的时间,它会尝试在真正运行基准测试前先尝试用较小的N运行测试来估算基准测试函数所需要的时间,然后推断一个较大的时间保证稳定的测量结果。
|
||||
|
||||
循环在基准测试函数内实现,而不是放在基准测试框架内实现,这样可以让每个基准测试函数有机会在循环启动前执行初始化代码,这样并不会显著影响每次迭代的平均运行时间。如果还是担心初始化代码部分对测量时间带来干扰,那么可以通过testing.B参数提供的方法来临时关闭或重置计时器,不过这些一般很少会用到。
|
||||
|
||||
现在我们有了一个基准测试和普通测试,我们可以很容易测试新的让程序运行更快的想法。也许最明显的优化是在IsPalindrome函数中第二个循环的停止检查,这样可以避免每个比较都做两次:
|
||||
现在我们有了一个基准测试和普通测试,我们可以很容易测试改进程序运行速度的想法。也许最明显的优化是在IsPalindrome函数中第二个循环的停止检查,这样可以避免每个比较都做两次:
|
||||
|
||||
```Go
|
||||
n := len(letters)/2
|
||||
@@ -42,7 +42,7 @@ for i := 0; i < n; i++ {
|
||||
return true
|
||||
```
|
||||
|
||||
不过很多情况下,一个明显的优化并不一定就能代码预期的效果。这个改进在基准测试中只带来了4%的性能提升。
|
||||
不过很多情况下,一个显而易见的优化未必能带来预期的效果。这个改进在基准测试中只带来了4%的性能提升。
|
||||
|
||||
```
|
||||
$ go test -bench=.
|
||||
@@ -89,9 +89,9 @@ BenchmarkIsPalindrome 2000000 807 ns/op 128 B/op 1 allocs/op
|
||||
|
||||
用一次内存分配代替多次的内存分配节省了75%的分配调用次数和减少近一半的内存需求。
|
||||
|
||||
这个基准测试告诉我们所需的绝对时间依赖给定的具体操作,两个不同的操作所需时间的差异也是和不同环境相关的。例如,如果一个函数需要1ms处理1,000个元素,那么处理10000或1百万将需要多少时间呢?这样的比较揭示了渐近增长函数的运行时间。另一个例子:I/O缓存该设置为多大呢?基准测试可以帮助我们选择较小的缓存但能带来满意的性能。第三个例子:对于一个确定的工作那种算法更好?基准测试可以评估两种不同算法对于相同的输入在不同的场景和负载下的优缺点。
|
||||
这个基准测试告诉了我们某个具体操作所需的绝对时间,但我们往往想知道的是两个不同的操作的时间对比。例如,如果一个函数需要1ms处理1,000个元素,那么处理10000或1百万将需要多少时间呢?这样的比较揭示了渐近增长函数的运行时间。另一个例子:I/O缓存该设置为多大呢?基准测试可以帮助我们选择在性能达标情况下所需的最小内存。第三个例子:对于一个确定的工作哪种算法更好?基准测试可以评估两种不同算法对于相同的输入在不同的场景和负载下的优缺点。
|
||||
|
||||
一般比较基准测试都是结构类似的代码。它们通常是采用一个参数的函数,从几个标志的基准测试函数入口调用,就像这样:
|
||||
比较型的基准测试就是普通程序代码。它们通常是单参数的函数,由几个不同数量级的基准测试函数调用,就像这样:
|
||||
|
||||
```Go
|
||||
func benchmark(b *testing.B, size int) { /* ... */ }
|
||||
@@ -102,7 +102,7 @@ func Benchmark1000(b *testing.B) { benchmark(b, 1000) }
|
||||
|
||||
通过函数参数来指定输入的大小,但是参数变量对于每个具体的基准测试都是固定的。要避免直接修改b.N来控制输入的大小。除非你将它作为一个固定大小的迭代计算输入,否则基准测试的结果将毫无意义。
|
||||
|
||||
基准测试对于编写代码是很有帮助的,但是即使工作完成了也应当保存基准测试代码。因为随着项目的发展,或者是输入的增加,或者是部署到新的操作系统或不同的处理器,我们可以再次用基准测试来帮助我们改进设计。
|
||||
比较型的基准测试反映出的模式在程序设计阶段是很有帮助的,但是即使程序完工了也应当保留基准测试代码。因为随着项目的发展,或者是输入的增加,或者是部署到新的操作系统或不同的处理器,我们可以再次用基准测试来帮助我们改进设计。
|
||||
|
||||
**练习 11.6:** 为2.6.2节的练习2.4和练习2.5的PopCount函数编写基准测试。看看基于表格算法在不同情况下对提升性能会有多大帮助。
|
||||
|
||||
|
Reference in New Issue
Block a user