0

我想要一些建议来优化我的代码,这是一个简单的代码,但它需要快速,快速我的意思是小于 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 的值

4

1 回答 1

1

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

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

由于 I/O 寄存器是易失的,因此优化器可能会在显着优化循环体时失败。但是由于它们是易变的,因此代码也可能是不安全的,因为有可能(并且确实很可能)在分配和PORTE之间更改值,这可能不是您的意图或可取的。如果是这种情况,则代码应更改如下:LATCLATD

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] ;
}

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

使用for(;;)thenwhile(1)可能没什么区别,但是一些编译器会针对不变的 while 表达式发出警告,bit 会for(;;)安静地接受这个习语。您没有包含第 13 行的代码汇编器,因此无法确定您的编译器生成了什么。

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

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

其中的地址是相邻和寄存器LATCD的低位地址,类型为。如果有较低的地址,则:LATCLATDunsigned long long intLATC

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

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

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

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

于 2015-11-01T19:06:47.697 回答