c - PIC32速度: Optimizing c code

标签 c assembly embedded pic32

我想要一些建议来优化我的代码,这是一个简单的代码,但它需要很快,我所说的快速是指小于 250 纳秒的时间。
我的第一个代码很慢,大约 1000 ns,但经过一些工作后,大约 550 ns 但我相信它可以做得更快,但我不知道如何:<
我使用的是具有 80 MHz 系统时钟的 PIC32
我的代码:

void main()
{
    unsigned long int arr_1[4095]; 
    unsigned long int arr_2[4095]; 

    //here I assign arr_1 and arr_2 values
    //...
    //...

    TRISC = 0;
    TRISD = 0;

    while(1){
         LATC = arr_1[PORTE];
         LATD = arr_2[PORTE];
    }

}

正如你所见,它作为一项工作非常简单,唯一的问题是速度。
我看到汇编列表只是为了看看有多少指令,但我不知道汇编语言可以优化它。

;main.c, 14 ::      LATC = arr_1[PORTE];
0x9D000064  0x27A30000  ADDIU   R3, SP, 0
0x9D000068  0x3C1EBF88  LUI R30, 49032
0x9D00006C  0x8FC26110  LW  R2, 24848(R30)
0x9D000070  0x00021080  SLL R2, R2, 2
0x9D000074  0x00621021  ADDU    R2, R3, R2
0x9D000078  0x8C420000  LW  R2, 0(R2)
0x9D00007C  0x3C1EBF88  LUI R30, 49032
0x9D000080  0xAFC260A0  SW  R2, 24736(R30)
;main.c, 15 ::      LATD = arr_2[PORTE];
0x9D000084  0x27A33FFC  ADDIU   R3, SP, 16380
0x9D000088  0x3C1EBF88  LUI R30, 49032
0x9D00008C  0x8FC26110  LW  R2, 24848(R30)
0x9D000090  0x00021080  SLL R2, R2, 2
0x9D000094  0x00621021  ADDU    R2, R3, R2
0x9D000098  0x8C420000  LW  R2, 0(R2)
0x9D00009C  0x3C1EBF88  LUI R30, 49032
;main.c, 16 ::      }
0x9D0000A0  0x0B400019  J   L_main0
0x9D0000A4  0xAFC260E0  SW  R2, 24800(R30)  

有什么建议可以优化我的代码吗?

编辑:
*PORTE、LATC 和 LATD 是 I/O 映射寄存器 *代码的目标是在 PORTE 更改时尽快更改 LATC 和 LATD 寄存器(因此 PORTE 是输入,LATC 和 LATD 是输出),输出取决于 PORTE 的值

最佳答案

一个潜在的限制因素是,由于 PORTELATCLATD 不是常规内存而是 I/O 寄存器,因此有可能I/O 总线速度低于内存总线速度,并且处理器在访问之间插入等待状态。对于 PIC32 来说,情况可能是这样,也可能不是,但这是任何架构都需要考虑的一般问题。

如果 I/O 总线不是限制,那么首先您是否应用了编译器优化?对于此类微优化,这通常是您的最佳选择。这段代码似乎经过了微不足道的优化,但汇编器似乎并没有反射(reflect)出这一点(尽管我不是 MIPS 汇编器专家 - 但编译器优化器却是)。

由于 I/O 寄存器是 volatile 的,因此优化器在优化循环体时可能会失败。但由于它们是 volatile 的,代码也可能是不安全的,因为 PORTE 有可能(而且确实有可能)在 LATC 的赋值之间更改值>LATD 这可能不是您的意图或不想要的。如果是这种情况,则代码应更改如下:

int porte_value_latch = 0 ;
for(;;)
{
     // Get a non-volatile copy of PORTE.
     porte_value_latch = PORTE ;  

     // Write LATC/D with a consistent PORTE value that 
     // won't change between assignments, and does not need 
     // to be read from memory or I/O.
     LATC = arr_1[porte_value_latch] ;
     LATD = arr_2[porte_value_latch] ;
}

这样既安全又可能更快,因为 volatile PORTE 只读取一次,并且 porte_value_latch 值可以保留在临时寄存器中以进行数组访问,而不是而不是每次都从内存中读取。即使常规编译不会,优化器几乎肯定会将其优化为寄存器访问。

使用 for(;;) 而不是 while(1) 可能没有什么区别,但有些编译器会对不变的 while 表达式发出警告,位会安静地接受for(;;)习惯用法。您尚未包含第 13 行的代码汇编器,因此无法确定编译器生成的内容。

如果 LATCLATD 位于相邻地址,则可以进一步进行优化,在这种情况下,您可以使用 unsigned 类型的单个数组long long int 以便在单个赋值中写入两个位置。当然,64 位访问仍然是非原子的,但编译器在任何情况下都可以生成更高效的代码。它还巧妙地避免了对 porte_value_latch 变量的需要,因为这样就只有一个对 PORTE 的引用。但是,如果必须按特定顺序编写 LATCLATD,您就会失去该级别的控制。循环看起来像:

for(;;)
{
    LATCD = arr_1_2[PORTE] ;
}

其中LATCD的地址是相邻LATCLATD寄存器的低位地址,并且类型为unsigned长长整型。如果 LATC 具有较低的地址,则:

unsigned long long int LATCD = (unsigned long long int)LATC ;

这样写入 LATCD 就会同时写入 LATC 和 LATD。然后,玩具必须将 arr_1arr_2 组合成一个具有适当字序的 unsigned long long 数组,以便它包含 C 和D 值在单个值中。

另一个建议:配置硬件以使用从 >=4MHz 的时钟信号触发的 DMA 将 PORTE 读取到单个位置。然后,循环根本不需要读取 PORTE,而是读取 DMA 内存位置,这可能会更快,也可能不会更快。您还可以将 DMA 设置为从内存位置写入 LATC/LATD,以便循环根本不执行 I/O。即使 LATC 和 LATD 实际上并不相邻,该方法也允许“相邻内存”方法起作用。

最终,如果问题仅在于编译器的代码生成,那么在内联汇编器中实现循环并手动优化它可能是有意义的。

关于c - PIC32速度: Optimizing c code,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33464795/

相关文章:

delphi - delphi XE2中如何从绝对地址读取数据

assembly - ARM Cortex-M4 互斥锁。 DMB指令

c++ - 为什么不建议在空间/辐射环境中使用 C++ 模板?

从 LPVOID 转换为结构体 - C

c - 在 Clang 编译器使用 printf 打印时从宏 'num' 扩展

assembly - MOVZX缺少32位寄存器到64位寄存器

c - 使用常量结构时为 "initializer element is not constant"

c - 如果您不释放微 Controller 中分配的内存会怎样?

c - 使用 dirent.h 浏览文件?

c - 无法将结构数组的变量作为函数参数传递