ZHCUCO7B December 2024 – November 2025 F29H850TU , F29H859TU-Q1
嵌入式系统中的许多操作涉及对存储器的连续数据访问(读取或写入)。由于 F29x 上的数据总线宽度为 64 位,此架构允许 64 位数据读取和写入。然而,大多数用户代码限制为 32 位数据,因此访问也限于 32 位。在某些情况下,尤其是数组访问,通过将代码重写为执行 64 位访问而非 32 位访问,可以实现显著性能提升。例如,下面的第一个代码块表示通过 32 位访问执行简单的内存缓冲区读取操作。
uint32_t mem_read_16k_cn(uint32_t *src)
{
uint32_t i =0;
uint32_t x =0;
for (i=0;i<LEN_16K;i++)
{
x +=*src++;
}
return x;
}下一个代码块表示使用 64 位访问实现的相同操作,其效率是原来的两倍。
uint32_t mem_read_16k_opt(uint32_t *src)
{
uint32_t i =0;
uint64_t *s2 = (uint64_t*) src;
uint32_t x =0;
uint32_t x2 =0;
for (i=0;i<LEN_16K>>1;i++)
{
uint64_t temp =*s2++;
x += (temp>>32);
x2 += (temp&0xFFFFFFFF);
}
return x+x2;
}在许多情况下,编译器无需显式重写代码,而是通过将特定属性应用于底层数据,即可提高性能。如果数组已对齐,请使用 __attribute__((aligned(val)) 向编译器指示这样做。例如,数组上的 __attribute__((aligned(8)) 指示 8 字节对齐,并允许编译器一次加载 64 位而不是 32 位。这可以为矩阵乘法等运算带来性能优势。如果数组是全局数组并在函数中直接访问,则上述对齐规范就足够了(请参阅下面的第一个代码块)。另一方面,如果对齐数组作为指针传递到函数中,则需要将 __builtin_assume_aligned 属性应用于指针,以告知编译器该对象的对齐情况(请参阅下面的第二个代码块)。
__attribute__((aligned(8))) float A[100];
__attribute__((aligned(8))) float B[100];
__attribute__((aligned(8))) float C[100];
void matrix_mpy(void)
{
int32_T i;
int32_T i_0;
int32_T i_1;
for (i_0 =0; i_0 <10; i_0++)
{
for (i =0; i <10; i++)
{
int32_T C_tmp, tmp;
C[10* i_0 + i] =0.0f;
for (i_1 =0; i_1 <10; i_1++)
{
C[10* i_0 + i] +=A[10* i_1 + i] *B[10* i_0 + i_1];
}
}
}void matrix_mpy_f32_4by4(float (*restrict Ma_f32) [4], float (*restrict Mb_f32)[4], float (*restrict Mc_f32)[4])
{
int32_t i,j,k;
// Use __builtin_assume_aligned to inform the compiler about alignment Ma_f32 = __builtin_assume_aligned(Ma_f32, 8);
Mb_f32 = __builtin_assume_aligned(Mb_f32, 8);
Mc_f32 = __builtin_assume_aligned(Mc_f32, 8);
..
}目前,编译器无法根据对象的物理地址(即仅基于位置属性)区分对齐。
这种“矢量化”(即将较小数据类型的访问合并到较大数据类型)也可以使用 8 位和 16 位数据进行,编译器的未来版本已计划纳入该功能。