23、嵌入式系统的时序优化策略

嵌入式系统的时序优化策略

1. 调度级别的时序优化

1.1 调度中断配置优化

在某些任务激活的时间片配置中,调度中断数量会对系统性能产生影响。例如,在可见时间片内所有任务激活时,一种配置需要六个调度中断,这对应着 Task_1ms 实例的数量;而之前的一种配置则需要十一个调度中断,几乎是前者的两倍。尽管如此,前一种配置的负载分布仍然是可接受的。随着任务数量的增加,不同配置之间的计算负载分布差异会更加明显。

1.2 任务拆分

当应用程序的部分功能通过后台任务实现,而较大部分位于周期为 5ms 的循环任务中,导致后台任务在循环任务执行期间无法执行时,可将循环任务拆分为两个周期为 5ms 但偏移量不同的循环任务。比如,将 Task_5ms 拆分为偏移量为 1ms 的 TaskA_5ms 和偏移量为 3ms 的 TaskB_5ms,这样可以为后台任务创造更多执行机会。不过,任务拆分是否适合某个项目,需结合项目实际情况判断,因为额外的任务会消耗额外的资源。

1.3 功能迁移至执行频率较低的任务

该优化方法虽看似简单,但在实际应用中十分有效。其核心在于思考是否可以降低所有循环执行代码段的执行频率。例如,如果确定一个可运行代码段每 10ms 执行一次而不影响功能,相比每毫秒执行一次,其运行时需求可降低 90%。对于控制算法,可将其拆分为每 10ms 处理通信的部分和在新创建的 15ms 任务中计算控制算法的部分,但在实施前需详细检查通信代码和控制算法之间的时间依赖关系。此外,该方法不仅适用于循环代码段,也可应用于非周期性、偶发性代码。

2. 内存使用的时序优化

2.1 快速内存的最优使用

微处理器具有不同的内存和寻址模式,快速内存和快速寻址模式通常资源有限。为实现软件的最高性能,应将频繁访问的小符号放置在快速内存中。因为快速内存空间有限,若分配大符号,会减少可存放的符号总数,降低有效访问次数。确定符号在内存中的位置时,需考虑符号的大小和访问频率。符号大小可通过查看链接器映射文件获取,而访问频率和函数调用频率则较难确定,可通过测量或跟踪的方式获得。在特殊情况下,编译器对函数进行插桩可能有用,但该方法存在诸多问题,如无法考虑无源代码的函数、代码和测量开销大、软件会被修改等。

2.2 数据对齐

数据对齐是指符号在内存中相对于一个虚拟网格的偏移量。以 32 位处理器为例,它要求 32 位数据的地址是 4 的整数倍,若数据地址不符合 32 位网格要求,处理器可能需要进行多次访问并手动组装结果,这会导致访问速度变慢。不同架构的处理器对数据对齐有不同要求,32 位架构通常对 32 位数据有对齐要求,16 位处理器对 16 位数据对齐更有效,8 位处理器相对较宽松。可通过在链接器脚本中为相应输出部分分配属性来影响数据对齐。若项目存在运行时问题且内存空间充足,引入不同输出部分的对齐可快速实施,虽会在内存中产生未使用的间隙,但能加快部分内存访问速度,效果需通过跟踪或运行时测量检查。此外,数据对齐在数据结构(struct)中也起着重要作用,创建结构时应尽量使不同大小的数据按对应对齐方式分组。

2.3 代码对齐和缓存优化

机器指令需按指令长度进行对齐存储,32 位处理器的 32 位指令需 32 位对齐,16 位指令需 16 位对齐。缓存作为快速中间内存,以缓存行为单位组织,其网格比数据对齐的网格更粗。全面的缓存优化成本较高,难以确定每个函数在内存中的最佳位置。若内存空间不是问题,可指定代码段按缓存行大小对齐,虽会增加内存需求,但对于能完全放入一个缓存行的小函数,可提高缓存使用效率。该措施的效果需通过跟踪或运行时测量检查。在代码和数据对齐方面,运行时优化通常会增加内存需求,因此需要在“最短运行时间”和“最低内存需求”两个优化目标之间做出选择。

3. 代码级别的时序优化

3.1 优化候选函数分类

代码级别的运行时优化应在考虑调度级别之后进行。通常,值得进行代码优化的函数可分为两类:
- 从很少甚至仅一个地方调用,但运行时需求高的函数,例如在单个大函数中进行的复杂计算。
- 经常被调用,且调用位置众多的函数,如数学函数(sin、cos、exp、sqrt 等)或数据类型转换函数。

以下是一个包含隐式类型转换的函数示例:

// Listing 37 Example of an implicit type conversion
int i = 42;
volatile double f;

void main(void)
{
    f = i;
}

其生成的汇编代码如下:

// Listing 38 Function call resulting from implicit type conversion
main:
    ld.w d4,i
    call __d_itod
    st.d f,e2
    ret

3.2 函数调用频率和 CPU 负载计算

为了有效进行代码优化,需要确定函数的调用频率 (f_F) 和净运行时间 (CET_F)。若分布足够均匀,可使用这两个参数的平均值计算函数的平均 CPU 负载 (U_F),计算公式为 (U_F = f_F \cdot CET_F)。例如,在某雷达软件中,正弦函数 sin 的平均调用频率 (f_F = 23200 Hz),平均净执行时间 (CET_F = 570 ns),则该函数占用 CPU 可用时间的 1.32%。

3.3 代码优化计划制定

在 CPU 完全过载的情况下,系统地测量所有函数并创建包含负责开发者和函数 CPU 负载的列表是很有用的。同时,可添加“预期成功”因素,该因素乘以 CPU 负载得到“优先级”,用于衡量优化某个特定函数的价值。制定代码优化策略时,应定义简单而可靠的标准,以控制代码优化过程,避免仅凭猜测进行优化,提高优化效率。

3.4 小而频繁调用函数的优化

对于小而频繁调用的函数,若确定其值得优化且有源代码,可同时查看源代码和编译器生成的汇编代码进行分析。尽管编译器可能会重新排序机器指令以优化运行时,但查看汇编代码仍有助于发现函数调用等情况。

以下是一个简单的 mermaid 流程图,展示代码优化的基本流程:

graph LR
    A[确定优化候选函数] --> B[测量调用频率和净运行时间]
    B --> C[计算 CPU 负载和优先级]
    C --> D{优先级高?}
    D -- 是 --> E[分析源代码和汇编代码]
    D -- 否 --> F[结束]
    E --> G[实施优化措施]
    G --> H[测量优化效果]
    H --> I{效果显著?}
    I -- 是 --> F
    I -- 否 --> E
优化方面 具体方法 注意事项
调度级别 优化调度中断配置、任务拆分、功能迁移 结合项目实际情况,考虑资源消耗和时间依赖关系
内存使用 快速内存使用优化、数据对齐、代码和缓存优化 注意内存空间限制,通过测量检查效果
代码级别 确定优化候选函数、计算 CPU 负载和优先级、优化小而频繁调用的函数 定义简单可靠的标准,避免盲目优化

3.5 优化小而频繁调用函数的具体操作步骤

当确定要优化小而频繁调用的函数时,可按以下步骤进行:
1. 收集信息
- 获取函数的源代码,如果没有源代码,则考虑是否可以从其他途径获取或者是否有替代方案。
- 使用测量或跟踪工具确定函数的调用频率和净运行时间。
2. 代码分析
- 同时打开源代码和编译器生成的汇编代码,将两者进行对比。
- 检查汇编代码中是否存在不必要的函数调用,尤其是一些库函数调用,看是否可以进行内联优化。
- 分析代码逻辑,看是否存在可以简化的计算或操作。
3. 实施优化
- 如果发现可以内联的函数调用,使用编译器的内联功能或者手动进行内联操作。
- 对代码逻辑进行优化,例如减少循环次数、避免重复计算等。
4. 效果验证
- 使用测量或跟踪工具再次测量优化后函数的调用频率和净运行时间。
- 计算优化后函数的 CPU 负载,与优化前进行对比,看是否有显著改善。

3.6 不同优化级别的协同作用

在实际的系统优化中,调度级别、内存使用级别和代码级别这三个层面的优化并不是孤立的,而是相互关联、相互影响的。例如,调度级别的任务拆分可能会影响到内存使用,因为增加了任务数量可能会导致内存需求增加;而代码级别的优化可能会影响到调度,因为优化后的代码执行时间变化可能会改变任务的调度顺序。

以下是一个 mermaid 流程图,展示不同优化级别之间的协同关系:

graph LR
    A[调度级别优化] --> B[内存使用级别优化]
    B --> C[代码级别优化]
    C --> A
    A --> D[系统性能提升]
    B --> D
    C --> D

3.7 优化实践案例

假设我们有一个嵌入式系统,该系统的 CPU 负载过高,导致系统响应变慢。我们可以按照以下步骤进行优化:
1. 调度级别优化
- 分析任务的调度情况,发现某些任务的调度中断配置不合理,进行优化调整,减少不必要的调度中断。
- 对一些运行时间较长的循环任务进行拆分,为后台任务提供更多执行机会。
- 评估一些循环执行的代码段,看是否可以将其执行频率降低,同时不影响系统功能。
2. 内存使用级别优化
- 分析符号的访问频率和大小,将频繁访问的小符号移动到快速内存中。
- 检查数据的对齐情况,通过在链接器脚本中设置属性,对数据进行对齐优化。
- 考虑代码的对齐和缓存优化,在内存空间允许的情况下,按缓存行大小对齐代码段。
3. 代码级别优化
- 确定值得优化的函数,计算其调用频率和 CPU 负载,制定优化计划。
- 对小而频繁调用的函数进行分析和优化,例如内联优化、代码逻辑简化等。

通过以上综合优化措施,系统的 CPU 负载得到了有效降低,系统性能得到了显著提升。

3.8 总结

在嵌入式系统的开发过程中,时序优化是提高系统性能的关键环节。通过调度级别、内存使用级别和代码级别三个层面的优化,可以有效降低 CPU 负载,提高系统的响应速度和稳定性。在进行优化时,需要根据系统的实际情况,综合考虑各个层面的优化措施,同时注意不同优化措施之间的协同作用。此外,优化过程中需要通过测量和跟踪工具对优化效果进行验证,确保优化措施的有效性。

优化层面 主要目标 具体优化方法 效果验证方式
调度级别 合理分配任务执行时间,减少调度开销 优化调度中断配置、任务拆分、功能迁移 观察系统任务执行情况,测量 CPU 负载变化
内存使用级别 提高内存访问效率,减少内存等待时间 快速内存使用优化、数据对齐、代码和缓存优化 测量内存访问时间,观察内存使用情况
代码级别 降低函数执行时间,减少 CPU 占用 确定优化候选函数、计算 CPU 负载和优先级、优化小而频繁调用的函数 测量函数调用频率和净运行时间,计算 CPU 负载变化

总之,嵌入式系统的时序优化是一个复杂而系统的工程,需要开发者具备扎实的专业知识和丰富的实践经验,通过不断地尝试和验证,才能找到最适合系统的优化方案。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值