我想要一些建议来优化我的代码,这是一个简单的代码,但它需要很快,我所说的快速是指小于 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 的值
最佳答案
一个潜在的限制因素是,由于 PORTE
、LATC
和 LATD
不是常规内存而是 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 行的代码汇编器,因此无法确定编译器生成的内容。
如果 LATC
和 LATD
位于相邻地址,则可以进一步进行优化,在这种情况下,您可以使用 unsigned 类型的单个数组long long int
以便在单个赋值中写入两个位置。当然,64 位访问仍然是非原子的,但编译器在任何情况下都可以生成更高效的代码。它还巧妙地避免了对 porte_value_latch
变量的需要,因为这样就只有一个对 PORTE
的引用。但是,如果必须按特定顺序编写 LATC
和 LATD
,您就会失去该级别的控制。循环看起来像:
for(;;)
{
LATCD = arr_1_2[PORTE] ;
}
其中LATCD
的地址是相邻LATC
和LATD
寄存器的低位地址,并且类型为unsigned长长整型
。如果 LATC
具有较低的地址,则:
unsigned long long int LATCD = (unsigned long long int)LATC ;
这样写入 LATCD 就会同时写入 LATC 和 LATD。然后,玩具必须将 arr_1
和 arr_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/