数据流分析之WorkList Algorithm

目录

 

(1)如何求解数据流方程?

(2)WorkList Algorithm


(1)如何求解数据流方程?

不论是Reaching Definition Analysis中对可达定义集合的求解,还是Liveness Analysis中对活跃变量集合的求解,本质上都是在解方程。

以RDA举例,其数据流方程如下:

\large IN(p) = \cup OUT(p_s),p_s\in pred(p) \ (1) \\OUT(p)=(IN(p)- defs(v))\cup \{(p,v)\} \ (2)

可认为每个指令都对应方程中的两个变量,即IN(p)和OUT(p)。为了减少变量个数,把(1)式带入(2)式,以去掉IN(p):

\large OUT(p)=(\cup OUT(p_s)- defs(v))\cup \{(p,v)\} ,p_s\in pred(p)

此时方程中的变量为\large OUT(p),p \in S_{instruction},而\large defs(v)\large \{(p,v)\}则属于常数,因为其值随着指令的确定而相应确定。

此外,方程还有一个初值,即,

\large IN(first\ program\ point) =\{\ \}

转化为OUT,

\large OUT(first\ program\ point) =\{something\}

 

为了说明求解思路,先用更简化的符号来表示上述方程:

\large y_1 = c \ (1)\\ y_2 = f_2(y_1,y_2,...,y_n) \ (2)\\ ...\\ y_n= f_n(y_1,y_2,...,y_n) \ (n)

通过此方程可以看出,数据流方程的一个重要特点就是,待求的每个未知数既是若干方程的自变量,也是某个方程的因变量。


从此特点出发,我们很快发现一种求解的思路,即先给定一组未知数的初始值如\large \{y_1^0,y_2^0,...,y_n^0\},接着将这组值带入上述方程,得到又一组未知数的值\large \{y_1^1,y_2^1,...,y_n^1\},如果\large \{y_1^0,y_2^0,...,y_n^0\}\large \{y_1^1,y_2^1,...,y_n^1\}相等,则说明找到了一组方程的解,因为这组值可让上述方程成立。如果\large \{y_1^0,y_2^0,...,y_n^0\}\large \{y_1^1,y_2^1,...,y_n^1\}不相等,则重复上述过程,一定能找出方程的一组解。原因如下:

 

对于RDA,自变量\large OUT(p_s),p_s\in pred(p)越大(即集合的基数越大),根据方程\large OUT(p)=(\cup OUT(p_s)- defs(v))\cup \{(p,v)\} ,p_s\in pred(p),其因变量\large OUT(p)也相应越大。

由此可知,上述方程中的函数\large f_1,f_2,...f_n都是单调不减的,即如果\large \{y_1^{k+1},y_2^{k+1},...,y_n^{k+1}\}\geq \{y_1^{k},y_2^{k},...,y_n^{k}\},则有

\large f(y_1^{k+1},y_2^{k+1},...,y_n^{k+1})\geq f(y_1^{k},y_2^{k},...,y_n^{k}),即

\large \{y_1^{k+2},y_2^{k+2},...,y_n^{k+2}\}\geq \{y_1^{k+1},y_2^{k+1},...,y_n^{k+1}\}

要让解集合\large \{y_1^{i},y_2^{i},...,y_n^{i}\}随着迭代的进行而越来越大(即集合的基数越大),还需要一个初始条件,即

\large \{y_1^1,y_2^1,...,y_n^1\}\geq \{y_1^0,y_2^0,...,y_n^0\}

如果令\large \{y_1^0,y_2^0,...,y_n^0\}=\{\{\ \},\{\ \},...,\{\ \}\},则有

\large \{y_1^1,y_2^1,...,y_n^1\}\geq \{\{\ \},\{\ \},...,\{\ \}\}=\{y_1^0,y_2^0,...,y_n^0\}

此时初始条件和函数单调性共同作用,有如下链式反应:

\large \{y_1^{k+1},y_2^{k+1},...,y_n^{k+1}\}\geq \{y_1^{k},y_2^{k},...,y_n^{k}\}\geq...\geq\{\{\ \},\{\ \},...,\{\ \}\}

由于\large y_1,y_2,...y_n是离散值(集合)且有上界(集合元素有上限),因此上述链式反应一定会终止,终止时会有

\large \{y_1^{i+1},y_2^{i+1},...,y_n^{i+1}\}= \{y_1^{i},y_2^{i},...,y_n^{i}\},这即为方程的一组解。

 

(2)WorkList Algorithm

上述思路对应的算法为Round-Robin Iterations,即各未知数的迭代次数(or更新次数)保持同步,即算法每次循环结束后都有

\large \{y_1^k,y_2^k,...,y_n^k\},而WorkList Algorithm则不同,其未知数的迭代次数不一定同步,在算法每次循环结束后可能会出现

\large \{y_1^{k+5},y_2^{k+1},...,y_i^k,...,y_n^{k-2}\},这说明针对未知数的值的更新,WorkList Algorithm有一套优先级规则。(其实这两种更新思路与数值分析中求解线性方程组的Jacobi迭代和Gauss-Seidel迭代有共通之处)

 

先给出WorkList Algorithm的(不规范的)伪代码:

WorkList()
    for i <- 1 to n do
        y[i] <- { } //初始化随具体情况而定
    w <- [ y[1] y[2] ... y[n] ] //将未知数放入worklist中
    while(!is_empty(w)) do 
        y[i] <- extract(w) //从worklist以某种方式抽出一个未知数
        old <- y[i] //记录此未知数的值
        y[i] <- f_i(y[1], y[2], ..., y[n]) //挑出以此未知数作为因变量的方程,并进行运算,结果赋给此未知数
        if old != y[i] then //如果未知数的值在运算后发生变化
            for y[k] <- dep(y[i]) do 
            //将依赖它的那些未知数放入worklist中,a依赖b的含义是
            //以a为因变量的方程使用b作为自变量,因此当b发生变化,a也就可能发生变化
            //故把a放入worklist中
                w <- insert(w, y[k])
        

未完待续

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章