WCET Worst Case Execution Time最坏情况执行时间。
- 一个计算机任务的最坏执行时间就是这个任务在特定的硬件平台上执行所需要的时间长度的最大值。最坏执行时间是在硬件实时系统调度分析中最重要的事情。
- WCET分析考虑一个独立任务的执行时间。在这个层面上,忽略和考虑的任务无关的活动,认为任务永远不会阻塞或者中断。
- 在高级层面,通过WCET分析对每一个任务或者程序的分析结果得到系统的总体性能。
- 一个实时系统通常由一系列的实时任务组成,这些任务共同完成预定的系统功能。
- 分析任务的WCET,必须考虑任务在具体硬件体系结构下的行为及产生的时间延迟。在硬件体系结构中,Cache对程序的执行时间影响最大。
- 在大多数处理器中,访存如果在Cache中命中,则时间延迟通常只有几个时钟周期。如果不命中,要从主存加载到Cache,时间延迟为100-200个时钟周期。因此,Cache分析的精确性对任务的执行时间有决定性的影响。
Cache工作原理
Cache是处理器片内的一个高速、小容量的存储器件。
- 程序在执行过程中会按照控制流程依次执行一系列指令,并操作相应的数据,指令和数据需要从存储器中加载。
- 处理器首先在Cache中查找所需要的数据是否存在:如果存在,就从Cache中取出数据送至CPU。否则,通过内存总线从主存中载入数据。
- 根据局部性原理,程序通常会在某个时间段内集中访问一部分指令或数据,想当数量的访存会在Cache中命中,所以Cache能够显著提高程序的执行性能。
- 多数处理器通常配置两级Cache:第一级Cache(L1 Cache)又被分为独立的指令Cache和数据Cache,分别用于缓存指令和数据。
- L1 Cache的访问延迟通常为13个时钟周期,容量大约为16KB32KB。
- 如果访问L1 Cache不命中,则需要到二级Cache中查找所需数据。L2 Cache的容量通常在几百KB到几MB,访问延迟通常在10个时钟周期左右。
- 在一些高性能多核处理器中,通常还配备L3 Cache,供所有处理核心共享。
- 如果数据或指令在末级Cache都不命中,就需要从主存载入所需内容。这一过程需要通过内存总线,访问延迟非常高,大约100~200个时钟周期。
- 向Cache载入数据的最小单位称为内存块,大小一般为8~64个字节。
- 目前,大多数处理器采用组相联的方式组织Cache地址空间。在组相联Cache中采用二维编址:地址空间首先被划分为若干个组,每个组内包含若干路,每路存放一个内存块。每组中路的数量称为相联度。
- 将一个内存块载入Cache时,首先根据地址确定唯一对应的组,如果组内空间不足,就根据某种替换策略选择一个内存块进行替换。
- 替换策略对程序的运行时间产生显著影响。
经典静态WCET分析框架
- 静态WCET分析的目标是:在不实际执行程序的情况下,通过数学手段分析得到程序的WCET估计值。本质上,程序的WCET对应于“在特定的初始状态和数据输入下,程序按某条路径的一次执行情况”。
- 如果程序的执行不被打断,所有可能的执行情况的集合S可粗略地表示为S = PxDxI.
- P:所有可能的程序执行路径的集合。
- D:所有可能的数据输入的集合
- I:程序启动前所有可能的初始状态的集合。
- 一种直接的分析思路:穷举程序所有可能的执行情况。但程序大多包含循环结构,循环体内部通常又包含分支结构,故程序路径的数量随循环内部分支数量和循环次数呈指数关系。
- 穷举所有执行情况的方法的可扩展性必定很差,静态分析不采用此方法。
- 首先对程序的可执行代码进行控制流分析。在对可执行代码进行反编译后,抽取出程序的控制流程图(control flow graph,CFG).
- 程序的CFG是一个有向带环图,图的节点为每一条指令,图的边表示程序的控制流程。
- 程序的CFG有唯一的起点和终点,分别表示程序的入口和出口。后续步骤都针对程序的CFG进行。
- 然后进行处理器行为分析。指令的执行时间受到流水线、分支预测器、Cache等处理部件的影响,呈现较大波动。
- 处理器行为分析的目标,在给定的处理器特性参数下,为CFG中的每条指令估计一个WCET的上限值。
- 最后一步就是找到导致整个程序执行时间最长的路径,称为WCET计算。
- 隐式路径枚举(implicit path enumeration)。将WCET求解问题(最长路径搜索问题)转化为求每条指令执行次数的问题,并将其建模成一个整数线性规划问题进行求解。
- WCET = max(∑Ci * Xi)
- i从1~N,表示指令的条数,Ci表示通过处理器行为分析得到的第i条指令的WCET估计值;Xi是为分析引入的变量,表示第i条指令的执行次数。
- 每条指令的执行次数并不可以取任意值,它们必须满足一系列的约束。
- 指令的执行次数首先要满足程序结构的约束。定义di_j为从指令i到指令j的边的执行次数,那么对于任何一条指令,其指令执行次数等于所有入口边执行次数之和,也等于所有出口边执行次数之和。
- 还需要建立程序功能约束。对于任何循环体,需要指定该循环体的最大循环次数。这一约束也可以通过不同节点执行次数之间的线性关系进行表达,并合并到上述线性规划问题中。通过求解WCET = max(∑Ci*Xi),即可得到程序的WCET估计值,以及对应的每条指令的执行次数,多条连续执行(中间不存在分支)的指令组成基本块,多数分析以基本块为单位,能提高分析的效率。
- 在这一框架中,最长路径搜索和程序行为分析这两步是分离的,对于一条多次执行的指令而言,每次执行所耗费的时间客观上都可能是不同的。
- 为每条指令计算WCET估计值,以此代表该指令
每次执行的时间。在此基础上,再寻找导致程序最大执行时间的路径。
处理器特性是影响程序执行时间的关键因素。
Cache分析核心技术
给定一种Cache体系结构,程序在Cache上的行为特点是一种客观事实。
- 无论何种分析技术,其根本目的都是视图挖掘程序的行为特征,并最终获得指令和数据在Cache中命中与否的信息。
- 程序在Cache中的行为是两个因素的结果:1)程序自身特性。2)Cache体系结构特性。
- 程序自身特性:程序执行不同的路径,会造成不同的访存序列,因而导致在Cache中的行为结果不同。区分指令和数据,它们的访存行为大相径庭。
- Cache体系结构特性:不同Cache替换策略、不同Cache级数、Cache是否共享等因素,都会对程序在Cache中的行为产生决定性影响。
- 为了达到Cache分析的目标,需要对程序的行为和Cache的工作原理分别进行建模。
- 抽象程度越高,分析效率就越高,但是由于信息损失严重,分析的精确性也会变差。
基于模型检测的Cache分析方法
模型检测(model checking)是进行系统分析与验证的典型手段之一。
- 在实时系统中,通常采用时间自由机(timed automata)来建模系统行为,特别是时间行为。
- 采用UPPAAL模型检测器将系统建模成若干时间机,通过验证的手段得到程序的WCET。
- 吕鸣松等人开发了WCET分析工具McAiT。
- McAiT分析工具基于模型检测技术,包括了两个主要的功能。
- 首先,根据程序的控制流程图建立程序自动机(program automata)。程序自动机是对程序的完整建模,既包含了程序的执行路径、控制流程信息,也建模了程序每条执行的指令对Cache的访问行为。
- 其次,根据给定的Cache参数描述建立Cache自动机,该自动机完整地描述了Cache的工作原理,并对具体Cache状态进行建模。
- 程序自动机因执行指令或访问数据发起Cache访问,通过UPPAAL提供的通道机制,向Cache自动机发送消息并传递Cache访问参数,Cache自动机根据这一信息响应地更新Cache状态。在UPPALL中,可以通过提取验证过程中时钟变量的上限来得到程序的WCET值。
- 基于模型检测技术的Cache分析方法,本质上是一种搜索程序的最坏执行情况的手段。如果采用自动机模型对系统行为进行了完全精确的建模,那么最终找的就是程序的最坏执行情况,得到的也是程序客观的WCET。
- 这类分析方法能够精确地告诉分析者,程序对Cache的每次访问是否命中。因此,基于模型检测的Cache分析方法具有最高的精确度。但是程序路径的数量随循环内部的分支数量和循环次数呈指数关系,所以基于模型检测技术的分析方法的可扩展性通常很差。对于大程序,会遭遇状态空间爆炸问题,难以在合理的时间和空间范围内得到分析结果。
基于抽象解释的Cache分析方法
Cache分析这一问题存在自身的复杂性,在实际分析中,大多数分析技术通常需要对程序以及Cache状态进行一定程度的抽象,通过适当地牺牲分析精度来获取分析效率的大幅度提升。
- 基于抽象解释理论的Cache分析方法,因为兼具很高的分析效率和分析精度,已经成为Cache分析领域具有统治地位的分析手段。
- 客观上,不同访存在Cache中的行为特征是不一样的,有些访存的行为规律较为明显,而有些行为规律难以描述。
- 基于抽象解释的Cache分析视图判定访存是否符合3种访问特性,这些特性被称为“Cache命中/失效分类(cache hit/miss classification CHMC)”
Cache命中/失效 分类 | 所反映的Cache行为特征 |
---|---|
一定命中(always hit)AH | 每次访问该指令,一定在Cache中命中 |
一定失效(always miss)AM | 每次访问该指令,一定不在Cache中命中 |
首次失效(first miss)FM | 第一次访问该指令失效,之后各次访问都在cache中命中 |
无法确定(not classified)NC | 不能被归为以上三类中任意一类 |
- 客观上,程序的行为特征并非有AH,AM,FM三种,而是因为主流的基于抽象解释的分析方法关注这三类行为特征。
- 如果一个访存无法被Cache分析归类为上述任何一种,那么称该访存为“无法确定”.
- 造成这一结果的原因有两个:这种访存客观的行为特征不符合以上三种分类;分析方法不够精确。
- 为保证分析的安全性,判定访存x对Cache的访问是否满足某种性质,需要检查从程序的初始点出发,经过所有可能的程序路径,最终到达x之前的所有具体Cache状态(concrete cache state,CSS),确定对于每个具体状态该性质都成立,把每条指令访问前的状态定义为一个程序点,那么每个程序点上都存在这样的一个具体Cache状态集合。
- 具体Cache状态集合中的状态数量呈指数爆炸的(与程序的执行路径的数量有关)。如果利用具体Cache状态进行分析,可扩展性会非常差。
- 所以,基于抽象解释的Cache分析技术在每个程序点维护一个抽象Cache状态,抽象表示该程序点上的所有具体Cache状态。
- 分析的过程也是针对抽象状态进行的,经过抽象,Cache状态空间被大大缩减,由此带来分析效率的显著提高。
- 下面以分析AH分类为例,说明基于抽象解释的Cache分析方法的基本思想。用来分析AH的方法称为MUST分析。
- 一个访存x能够被判定为AH,必须保证x紧前的程序点上的所有具体Cache状态中都包含x,那么接下来对x的访问必然命中。
- 根据这一性质,MUST分析在抽象状态中为每个内存块维护年龄的上限值。对于任何一个内存块,在访问它之前的程序点上,如果抽象状态中的年龄上限小于Cache的相联度A,则能够断定该访存一定命中。
- 假设Cache是4路组相联,讨论对x的访问。假设从程序起始点到x有两条路径,通过这两条路径之前的具体Cache状态为c1,c2。可以对这两个护体状态进行抽象,得到状态c’,仅当一个内存块出现在所有具体状态中时,它才会出现在抽象状态c’中,且该内存块的年龄(所在位置)为它在c1,c2中的最大年龄。
- 因此,抽象Cache状态中的每一路维护的是内存块的集合。如果此时访问x,根据LRU替换策略的工作原理,具体状态c1和c2会更新为c1’和c2’。
- 与此同时,在抽象域中,访问x造成抽象状态的更新,更新行为称为MUST分析的更新函数,更新后抽象状态c’'依然能够正确维护每个内存块的年龄上限。
- 访问x导致抽象状态中所有内存块的年龄都增加1。
- 基于抽象解释的分析在每个程序点上维护唯一的抽象状态,但这一个程序点上可能有多条入口,每条路径上都可能携带一个抽象状态,所以需要将多个抽象状态合并为一个抽象状态。
- 仅当一个内存块出现在所有入口抽象状态中,它才会保留在合并后的抽象状态中。
- 内存块的年龄为它在所有入口抽象状态中年龄的最大值。
- 在分析启动时,各个程序点上起始的抽象状态是不包含任何程序执行信息的。为了正确判定访存的CHMC,必须为每个程序点构造一个能够包含所有具体Cache状态的抽象状态。
- 在每轮分析中,对于每个访存,根据访存输入程序点第i-1轮的抽象状态,利用相应的抽象域更新函数,计算得到该访存输出程序点第i轮的抽象状态。若发现相邻的两轮中每个程序点上的抽象状态不变,则表明不动点以及达到,迭代结束。
- 此后,根据每个访存之前的抽象状态,判定访存的CHMC分类。在MUST分析中,如果一个访存出现在它之前的MUST抽象状态中,则判定该访存为AH,否则不是AH。
- 以上是使用MUST分析判定AH的完整过程。
- 在基于抽象解释的Cache分析技术中,判定AM分类是通过MAY分析来完成的。MAY分析的工作过程与MUST分析非常类似,,主要区别在于MAY抽象域的定义,以及该抽象域下的更新与合并函数语义。
- MAY分析在抽象Cache状态中维护每个访存的年龄下限,如果不动点到达后,访存的年龄下线大于Cache的相联度A,则判定该访存在Cache中一定不命中。
- 首次失效(FM)。决定程序执行时间的主要因素是循环体结构,程序局部性原理告诉我们,在大多数情况下,程序的循环体是能够被放入Cache中的。
- 在第一次执行循环体时,需要将其载入Cache,产生冷失效;在以后各轮循环执行过程中,循环体内部的访存将是命中的。所以这些访存的行为特征呈现为首次失效。
- 在基于抽象解释的分析技术中心,PERSISTENCE分析用于判定FM分类,其抽象域类似于MUST抽象域和MAY抽象域的结合。类似于MAY抽象域,PERSISTENCE抽象域记录所有可能被访问的元素的信息,以捕捉“元素被载入Cache”这一行为。类似于MUST抽象域,PERSISTENCE分析为每个访存维护年龄上限,用于判定元素被载入后是否一直留在Cache中。
- MUST和MAY和PERSISTENCE分析完整地构成了基于抽象解释的Cache分析技术。客观上,访存的行为特性不限于AH,AMFM三类,但实际证明基于抽象解释的Cache分析技术有很好的精确性,说明LRU替换策略下的指令Cache,这3中访问特性分类足够覆盖指令访存的大部分情况。
cache状态转换图
- 静态Cache分析技术,该技术用Cache冲突图/Cache转换图(cache state translation graph)来建模Cache中的替换行为。
- 在分析组相联Cache时,需要为每个Cache组建立CSTG图(有向带环图)。图中节点为所有可能的具体Cache状态,节点之间的有向边表示存在程序的某种执行情况,可以导致从起点Cache状态迁移到终点Cache状态。
- 根据CSTG图中信息,可以为每个访存的命中次数建立线性约束。
- 把这些Cache行为的线性约束和程序执行的线性约束合并为一个整数线性规划问题,就可以求解程序的WCET。同时,也能从此时的变量取值获得最坏情况下每个访存具体的Cache命中次数。
- 可以从不同角度对基于CSTG的Cache分析技术进行评价。从抽象程度来将,CSTG方法几乎是Cache状态的枚举,显然,抽象程度低于抽象解释分析技术。
- 较低的抽象程度导致CSTG有较高的分析精度,但是分析效率低下,以及可扩展性差,是此方法的根本问题。
- 假定组相联Cache的相联度为A,映射到某一个Cache组的内存块的个数为M。
- 从分析的本质结果来看,CSTG方法的分析目标也与抽象解释分析差别很大。
- CSTG方法用线性约束建模Cache行为,最终得到的是每个访存在最坏情况下的命中次数,从某种角度讲,这是关于Cache命中行为的一种数量统计。而抽象解释分析则试图挖掘诸如AH,AM,FM这些不变式所描述的确定访问规律。
- 显然,CSTG对Cache访问特性的表达比基于抽象解释的方法更具有一般性。
其他维度上Cache分析问题和相关技术
循环结构的分析
- 之前介绍了基于抽象解释的Cache分析技术,其中一个隐含假设是:将一个程序看做整体进行分析,得到的结果自然也是从全局角度所刻画的Cache访问特性。
- 但是当程序中存在复杂循环结构时,上述方法可能得到过于悲观(甚至不可用)的分析结果。
- 假设Cache采用LRU替换策略,2路组相联,每个访存如果在Cache中命中,时间延迟为1,否则为10.各层循环的执行次数为10.
- 外层嵌套循环有a,b,c,d这4个内存块映射到同一Cache组中,c和d属于内层循环。假定外层循环入口处的Cache状态为空,如果采用抽象解释分析讲这个嵌套循环当做一个整体来分析,则分析结果将是a和b判定为AM,c和d判定为NC.在WCET计算过程中,NC被当做AM处理,所以这个嵌套最终计算得到的WCET值为2200.
- 所以将嵌套循环视为整体的分析方法得到的WCET过于悲观。
- 因此Cache分析要挖掘访存在各层循环级别上的局部访问特性。有两种分析技术:VIVU(Virtual inlining and virtual unrolling)与多层PERSISTENCE分析。
- VIVU技术的思路是:通过展开循环体区分循环的首次执行和其它各次执行,对展开后的CFG进行分析,以发现循环体在首轮和其它各轮被访问时的不同特性。cf和df表示内层循环的首次执行,co和do表示内层循环其它各轮执行。
- 对展开的CFG进行MUST分析可以发现co和do都被判定为AH,可以说明每次进入内存循环,内存块c除首轮之外的执行都是命中的。
- 多层PERSISTENCE分析技术,分别针对各层循环体进行PERSISTENCE分析,得到的结果就是在对应循环层次上的FM特性。
- 首先外层循环进行PERSISTENCE分析,没有任何一个访存可以被判定为FM,在对内层循环体的局部CFG进行PERSISTENCE分析,c和d能被判定为FM。
- 对于更复杂的程序,两种方法可能得到不同的分析结果。假定Cache为3路组相联,所有出现的内存块都映射到同一Cache组。
- 对于图a,采用CICU方法,co和do因为位于两个分支中,都无法被MUST分析判断为AH,最终cf,df,co,do都被判定为NC。采用PERSISTENCE分析方法,通过对b,c,d所在的内层循环体的局部分析,3个内存块都能被判定为FM。
- 对于图b,运用PERSISTENCE分析,b,c,d,e四个内存块都无法判定为FM。如果采用VIVU方法,尽管MUST分析不能将co和do定为AH,但bo和eo是AH。因此在VIVU下,b和e的局部FM访问特性被显示出来。
- 因此对于VIVU技术和PERSISTENCE分析,没有任何一种方法严格好于另一种。两者精确性的优劣本质上是MUST抽象域和PERSISTENCE抽象域的精确性所决定的。
数据Cache分析
大多数处理器会配备独立的指令Cache和数据Cache,分别缓存程序指令和被操作的数据。
- 程序对指令的访问规律强,可预测性高,因此,现有技术分析指令Cache都能获得较高的分析精度。
- 但程序对数据的访问要复杂得多:首先,对于指令Cache而言,执行一条指令引入对唯一内存块的访问。对于数据Cache而言,一条指令可能导致对多个数据块的访问。
- 用一个嵌套循环实现矩阵乘法
for (i = 0; i < N; i++)
for(k = 0; k < N; k++)
for(j = 0; j < N; j++)
C[j][i] += A[k][i] * B[j][k]
- 同一语句一次操作多个矩阵的数据;在时间上,同一语句在循环的不同轮次里操作不同的数据(矩阵不同的行、列)。
- 正是因为程序对数据和指令的访问特性在空间和时间上有明显的不同,分析数据Cache比分析指令Cache复杂得多。
- 为了分析数据Cache,首先要确定执行每条指令时所有可能被访问的数据块的集合。
- 从体系结构的角度,如果对数据的寻址方式仅有直接寻址一种,那么被访问的数据地址可以直接从指令中得到。但是主流处理器都采用了复杂的多级寻址机制,被访问数据的地址并不是直接写入指令,而是存在于某些特定的寄存器中,所以确定访问数据的物理地址就比较困难。
- 从程序语言的角度,通常在程序中使用指针访问数据。在实际运行过程中究竟会访问哪些数据,是受程序语义控制的。因此作为Cache分析之前的准备工作,确定每条集合可能访问的数据集合是非常复杂的。
- 目前,分析数据访问地址的主要方法包括编译技术中常用的数据流分析技术以及基于抽象解释的分析技术。
- 从时间维度上发掘数据访问的局部性,并将这种时间局部性信息建模到分析所采用的抽象域中。
- 范围敏感(scope-aware)的数据Cache分析:对于循环体中的指令而言,即使在一点上可能访问多个数据,它们也是在循环的不同轮次被访问的。
- 针对这一规律,可以在传统的PERSISTENCE抽象域中为每个内存块标记访问时间范围信息,即每个内存块可能被访问的循环轮次。在进行更新时,如果内存块x的时间范围与抽象状态中y的时间范围无交叠,那么访问x不会增加y在抽象状态中的年龄。把每个内存块自身的行为特性及对其它内存块的影响都限定在了特定的循环轮次中。
- 为访存建立失效计数器,从程序全局的角度约束最多可能发生的失效的次数。
- 例如,执行循环中的某条指令可能访问a,b,c,d四个数据,但每次仅访问其中一个。在这个程序点上,由于不能确定具体访问4个数据中哪一个,因此更新语义是:每个访问都会造成抽象状态中内存块的年龄增加。如果Cache相联度为4,那么访问这4个内存块后,抽象状态中的所有其它内存块都已经被替换出Cache。每次指令执行只访问1个内存块,那么最多只能造成抽象状态中年龄最老的内存块被替换出去。
多级Cache分析
大多数是实际处理器采用多级Cache体系结构。以一个两级Cache为例,当程序需要访问数据或指令时,现在L1 Cache上找,如果不命中,再到L2 Cache查找;再不命中才会访问内存。尽管各级Cache的访问速度不同,由于Cache位于CPU内部,而访问内存却需要通过片外内存总线,末级Cache的访问速度也是远远高于内存的访问速度。为了获得精确的WCET估计值,必须对各级Cache进行有效的分析。
- 无论访问指令或数据,都一定首先访问L1 Cache。如果L1 Cache中命中,就不会造成L2 Cache的影响。因此,需要在相邻两级Cache之间建立“是否访问下级Cache”的访问关系模型。
- 其次,在多级Cache硬件设计中,相邻Cache之间的包含关系是一个重要的设计选项。典型的包含关系有两类:非包含结构和包含结构。
- 对于包含结构,要求第i+1级Cache必须严格包含第i级Cache的内容,对于非包含结构,则不做此要求。
- 给分析带来更大困难的是包含结构,这是因为如果某次访存造成某级Cache中的某个元素被替换出去,那么在替他各个级别上都需要将这个元素替换出去以保持包含特性。这种行为造成了相邻两级Cache上访问行为的相互依赖,给分析带来难度。
- 访问数据涉及对Cache的写操作。如果系统只有一级Cache,无论采用直写或写回方式,都不会对数据Cache的行为造成显著的影响。
- 但是在多级Cache结构下,采用写回或写直机制,甚至可能对分析的安全性造成影响。
- 早期思路:从第1级开始逐层分析各级Cache。根据第i级分析得到的Cache命中/失效分类,确定是否会访问第i+1级Cache。
- 但是这种方法是不安全的,如果一个访存在第i级Cache中判定为NC,那么在讨论第i+1级Cache时,会被当做AM处理。显然,在单级Cache分析中,将NC作为AM是标准的做法。
- 但是在多级Cache分析中,就会导致分析结果不安全。
- 首先,采用传统的抽象解释分析技术对第L级Cache进行分析,得到该级的CHMC分类;同时对于第L级Cache,定义Cache访问分类,CAC描述了对于一种特定的CHMC分类,是否会造成对下一级的访问。
- 根据第L级Cache的CHMC分析结果以及CAC分类,可以确定对L+1级Cache的CAC分类。