一個編寫良好的計算機程序經常具備良好的局部性,它們傾向於引用鄰近於其餘最近引用過的數據項的數據項,或者最近引用過的數據項自己,這種傾向性,被稱爲局部性原理。有良好局部性的程序比局部性差的程序運行得更快。git
function sum(arry) {
let i, sum = 0
let len = arry.length
for (i = 0; i < len; i++) {
sum += arry[i]
}
return sum
}
複製代碼
在這個例子中,變量sum在每次循環迭代中被引用一次,所以,對於sum來講,具備良好的時間局部性github
具備良好空間局部性的程序chrome
// 二維數組
function sum1(arry, rows, cols) {
let i, j, sum = 0
for (i = 0; i < rows; i++) {
for (j = 0; j < cols; j++) {
sum += arry[i][j]
}
}
return sum
}
複製代碼
空間局部性差的程序數組
// 二維數組
function sum2(arry, rows, cols) {
let i, j, sum = 0
for (j = 0; j < cols; j++) {
for (i = 0; i < rows; i++) {
sum += arry[i][j]
}
}
return sum
}
複製代碼
再回頭看一下時間局部性的示例,像示例中按順序訪問一個數組每一個元素的函數,具備步長爲1的引用模式。
若是在數組中,每隔k個元素進行訪問,就稱爲步長爲k的引用模式。
通常而言,隨着步長的增長,空間局部性降低。
瀏覽器
這兩個例子有什麼區別?區別在於第一個示例是按照列順序來掃描數組,第二個示例是按照行順序來掃描數組。
數組在內存中是按照行順序來存放的,結果就是按行順序來掃描數組的示例獲得了步長爲rows的引用模式; 而對於按列順序來掃描數組的示例來講,其結果是獲得一個很好的步長爲1的引用模式,具備良好的空間局部性。bash
對一個長度爲9000的二維數組(子數組長度也爲9000)進行10次空間局部性測試,時間(毫秒)取平均值,結果以下:
函數
所用示例爲上述兩個空間局部性示例性能
按列排序 | 按行排序 |
---|---|
124 | 2316 |
從以上測試結果來看,二維數組按列順序訪問比按行順序訪問快了1個數量級的速度。測試
const arry = []
let [num, n, cols, rows] = [9000, 9000, 9000, 9000]
let temp = []
while (num) {
while (n) {
temp.push(n)
n--
}
arry.push(temp)
n = 9000
temp = []
num--
}
let last, now, val
last = new Date()
val = sum1(arry, rows, cols)
now = new Date()
console.log(now - last)
console.log(val)
last = new Date()
val = sum2(arry, rows, cols)
now = new Date()
console.log(now - last)
console.log(val)
function sum1(arry, rows, cols) {
let i, j, sum = 0
for (i = 0; i < rows; i++) {
for (j = 0; j < cols; j++) {
sum += arry[i][j]
}
}
return sum
}
function sum2(arry, rows, cols) {
let i, j, sum = 0
for (j = 0; j < cols; j++) {
for (i = 0; i < rows; i++) {
sum += arry[i][j]
}
}
return sum
}
複製代碼