當(dāng)在進(jìn)行大量的計算時,提升性能最直接有效的一種方式就是避免重復(fù)計算。通過在內(nèi)存中緩存和重復(fù)利用相同計算的結(jié)果,稱之為內(nèi)存緩存。最明顯的例子就是生成斐波那契數(shù)列的程序(詳見第 6.6 和 6.11 節(jié)):
要計算數(shù)列中第 n 個數(shù)字,需要先得到之前兩個數(shù)的值,但很明顯絕大多數(shù)情況下前兩個數(shù)的值都是已經(jīng)計算過的。即每個更后面的數(shù)都是基于之前計算結(jié)果的重復(fù)計算,正如示例 6.11 fibonnaci.go 所展示的那樣。
而我們要做就是將第 n 個數(shù)的值存在數(shù)組中索引為 n 的位置(詳見第 7 章),然后在數(shù)組中查找是否已經(jīng)計算過,如果沒有找到,則再進(jìn)行計算。
程序 Listing 6.17 - fibonacci_memoization.go 就是依照這個原則實現(xiàn)的,下面是計算到第 40 位數(shù)字的性能對比:
內(nèi)存緩存的優(yōu)勢顯而易見,而且您還可以將它應(yīng)用到其它類型的計算中,例如使用 map(詳見第 7 章)而不是數(shù)組或切片(Listing 6.21 - fibonacci_memoization.go):
package main
import (
"fmt"
"time"
)
const LIM = 41
var fibs [LIM]uint64
func main() {
var result uint64 = 0
start := time.Now()
for i := 0; i < LIM; i++ {
result = fibonacci(i)
fmt.Printf("fibonacci(%d) is: %d\n", i, result)
}
end := time.Now()
delta := end.Sub(start)
fmt.Printf("longCalculation took this amount of time: %s\n", delta)
}
func fibonacci(n int) (res uint64) {
// memoization: check if fibonacci(n) is already known in array:
if fibs[n] != 0 {
res = fibs[n]
return
}
if n <= 1 {
res = 1
} else {
res = fibonacci(n-1) + fibonacci(n-2)
}
fibs[n] = res
return
}
內(nèi)存緩存的技術(shù)在使用計算成本相對昂貴的函數(shù)時非常有用(不僅限于例子中的遞歸),譬如大量進(jìn)行相同參數(shù)的運算。這種技術(shù)還可以應(yīng)用于純函數(shù)中,即相同輸入必定獲得相同輸出的函數(shù)。