【问题标题】:PIC32 speed : Optimizing c codePIC32 速度:优化 c 代码
【发布时间】:2015-11-01 17:07:26
【问题描述】:

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

【问题讨论】:

  • 代码永远不会退出,所以实际运行时间是“永远”。你到底在测量什么?很可能,大部分运行时间(“永远”循环除外)都用于设置数组值,而您没有发布重要的细节。
  • 你是如何编译/链接代码的?你在使用任何优化参数吗? (对于 gcc,建议 -o3
  • 您可能需要清楚 PORTE、LATC 和 LATD 是什么 - 特别是如果它们是 volatile 或 I/O 映射的。
  • @user3629249 我的衡量标准是更改 PORTE 时更改 LATC 和 LATD 所需的时间。
  • @Clifford 他们是 I/O 映射的

标签: c assembly embedded pic32


【解决方案1】:

一个潜在的限制因素是,由于PORTELATCLATD 不是常规内存而是 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] ;
}

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

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

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

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

其中LATCD的地址是相邻LATCLATD寄存器的低位地址,类型为unsigned long long int。如果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 实际上并不相邻,该方法也允许“相邻内存”方法工作。

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

【讨论】:

  • 首先,感谢您的回答。其次,如果 PORTE 在闩锁之后发生变化(我验证过),那么使用变量进行闩锁的代码可能会使 LATD 的时序变慢但是我喜欢 LATC 和 LATD 的相邻地址的想法(我应该调查)
  • @HisokaHunter :我的目的是让代码更安全——无论它是否更快都需要测试。它假定必须使用一致的索引值写入 LATC 和 LATD。如果不是这种情况,那么唯一的好处就是单次 PORTE 读取。您应该对其进行测试,因为这取决于限制因素是什么。
  • 我认为它的安全有两个原因:在 LATD 和 LATC 之间切换的顺序并不重要,读取 PORTE 是原子操作,因此可以随时更改 PORTE 而不会出现问题
  • @HisokaHunter : 你知道你的应用程序,在一般情况下,关于数据一致性的问题也许仍然是一个重要的考虑因素。
  • @HisokaHunter :我不认为 PORTE 更改和 LATD 设置之间的延迟特别有效。如果它在第一次读取后立即更改,则设置 LATD 仍会延迟设置 LATC 和第二次读取 PORTE 所需的时间长度 - 最坏情况的延迟可能会更长,即使最佳情况更短- 它降低了确定性。请记住,在任何情况下,PORTE 的更改都与 PORTE 的读取完全异步。最坏的情况发生在 PORTE 刚被读取后发生变化,而最好的情况发生在刚被读取之前。
猜你喜欢
  • 2012-05-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-08-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多