程序性能優化-局部性原理

更多文章

概念

一個編寫良好的計算機程序經常具備良好的局部性,它們傾向於引用鄰近於其餘最近引用過的數據項的數據項,或者最近引用過的數據項自己,這種傾向性,被稱爲局部性原理。有良好局部性的程序比局部性差的程序運行得更快。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

性能測試

運行環境

  • cpu: i5-7400
  • 瀏覽器: chrome 70.0.3538.110

對一個長度爲9000的二維數組(子數組長度也爲9000)進行10次空間局部性測試,時間(毫秒)取平均值,結果以下:
函數

所用示例爲上述兩個空間局部性示例性能

按列排序 按行排序
124 2316

從以上測試結果來看,二維數組按列順序訪問比按行順序訪問快了1個數量級的速度。測試

總結

  • 重複引用相同變量的程序具備良好的時間局部性
  • 對於具備步長爲k的引用模式的程序,步長越小,在內存中以大步長跳來跳去的程序空間局部性會不好

測試代碼

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
}
複製代碼

參考資料

深刻理解計算機系統ui

相關文章
相關標籤/搜索