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

更多文章

概念

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

局部性一般有兩種不一樣的形式:

  • 時間局部性:在一個具備良好時間局部性的程序中,被引用過一次的內存位置極可能在不遠的未來被屢次引用。
  • 空間局部性 :在一個具備良好空間局部性的程序中,若是一個內存位置被引用了一次,那麼程序極可能在不遠的未來引用附近的一個內存位置。

時間局部性示例github

function sum(arry) {
    let i, sum = 0
    let len = arry.length

    for (i = 0; i < len; i++) {
        sum += arry[i]
    }

    return sum
}

在這個例子中,變量sum在每次循環迭代中被引用一次,所以,對於sum來講,具備良好的時間局部性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的引用模式。
通常而言,隨着步長的增長,空間局部性降低。測試

這兩個例子有什麼區別?區別在於第一個示例是按行掃描數組,每掃描完一行再去掃下一行;第二個示例是按列來掃描數組,掃完一行中的一個元素,立刻就去掃下一行中的同一列元素。code

數組在內存中是按照行順序來存放的,結果就是逐行掃描數組的示例獲得了步長爲 1 引用模式,具備良好的空間局部性;而另外一個示例步長爲 rows,空間局部性極差。內存

性能測試

運行環境:get

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

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

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

步長爲 1 步長爲 9000
124 2316

從以上測試結果來看,步長爲 1 的數組執行時間比步長爲 9000 的數組快了一個數量級。

總結:

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

參考資料:

相關文章
相關標籤/搜索