我想要一些建议来优化我的代码,这是一个简单的代码,但它需要快速,快速我的意思是小于250 ns。
我的第一个代码很慢,大约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的值
答案 0 :(得分:1)
一个潜在的限制因素是,由于PORTE
,LATC
和LATD
不是常规内存而是I / O寄存器,因此I / O总线速度可能较低比内存总线速度和处理器在访问之间插入等待状态。对于PIC32,情况可能是也可能不是,但对于任何体系结构,您都需要考虑这一点。
如果I / O总线不是限制,那么首先应用编译器优化吗?对于这种微优化通常是您最好的选择。这段代码似乎很容易优化,但是汇编程序似乎没有反映出来(虽然我不是MIPS汇编程序专家 - 但编译器优化器却是这样)。
由于I / O寄存器是易失性的,因此优化器可能会在优化循环体时失败。但由于它们是易变的,因此代码可能也是不安全的,因为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)
的使用可能没什么区别,但是有些编译器会对表达式发出不变的警告,bit会静静地接受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 long long int
。如果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实际上不相邻也能工作的方法。
最终如果问题仅限于编译器的代码生成,那么在内联汇编器中实现循环并手动优化它可能是有意义的。