编译器优化如何加快简单操作之间的时间?

Posted

技术标签:

【中文标题】编译器优化如何加快简单操作之间的时间?【英文标题】:How is compiler optimization speeding up the time between simple operations? 【发布时间】:2014-09-22 13:22:31 【问题描述】:

我有一个使用 SPI 的嵌入式 C++ 项目。当我编译并运行我的程序而不进行优化 (-O0) 时,外围设备(LCD 面板)工作正常。当我使用优化 (-O1) 编译它时,外围设备无法正常工作。

我用逻辑分析仪检查了这两种情况,唯一的区别是写入字节之间的时间使用优化的代码要短得多(时钟速率、写入的字节数等是相同的)。编译器优化如何影响后续操作之间的时间,这些操作只是一个接一个地写入硬件寄存器?如果我在 SPI 类中的每个写入命令后添加延迟,它在优化的情况下工作。

与下面的示例不同,在原始代码中,对WriteCommand()WriteData() 的调用是通过指针进行的。

代码 sn-p,通过 SPI 连续写入外设:


    SPI m_spiPort();
    m_spiPort.Init();

    m_spiPort.WriteCommand(SLEEPOUT);

    // Color Interface Pixel Format (command 0x3A)
    m_spiPort.WriteCommand(COLMOD);
    m_spiPort.WriteData(0x03); // 0x03 = 12 bits-per-pixel

    // Memory access controller (command 0x36)
    m_spiPort.WriteCommand(MADCTL);
    m_spiPort.WriteData(0x00);

    // Write contrast (command 0x25)
    m_spiPort.WriteCommand(SETCON);
    m_spiPort.WriteData(0x39); // contrast 0x30

    // Display On (command 0x29)
    m_spiPort.WriteCommand(DISPON);


SPI 类:

class SPI 
public:
    void Init();
    void WriteCommand(unsigned int command);
    void WriteData(unsigned int data);
private:
    void Write(unsigned int value);
;

这个类的实现是:

void SPI::WriteCommand(unsigned int command)

    command &= ~0x100;   //clear bit 8
    Write(command);


void SPI::WriteData(unsigned int data)

    data |= 0x100;   //set bit 8
    Write(data);


void SPI::Write(unsigned int value)

    LPC_SSP->DR = value;

void SPI::Init( void )

  LPC_SYSCON->SYSAHBCLKCTRL |= (1<<11);  //Enables clock for SPI
  LPC_SYSCON->SSPCLKDIV = 0x01;

  LPC_IOCON->PIO0_14 &= ~(0x7); // SCK
  LPC_IOCON->PIO0_14 |= 0x2;

  LPC_IOCON->PIO0_17 &= ~(0x7); // MOSI
  LPC_IOCON->PIO0_17 |= 0x2;

  /* SSP SSEL is a GPIO pin */
  LPC_IOCON->PIO0_27 = 0x0;     // configure as GPIO pin
  LPC_GPIO0->MASK = (1<<27);
  LPC_GPIO0->DIR |= (1<<27);    // set in output mode */
  LPC_GPIO0->CLR = 1 << 27;

  /* Set DSS data to 9-bit, Frame format SPI, CPOL = 0, CPHA = 0, and SCR is 0 */
  LPC_SSP->CR0 = 0x0008;

  /* SSPCPSR clock prescale register, master mode, minimum divisor is 0x02 */
  LPC_SSP->CPSR = 0x4;  // SPI clock will run at 6 MHz

  /* set Master mode and enable the SPI */
  LPC_SSP->CR1 = 0x2;

编辑 - 从 SPI::Write() 中删除了 DelayInCycles()。没有它,差异仍然很明显,我并不想在这篇文章中包含它。

【问题讨论】:

在哪个目标上,使用哪个编译器?优化通常会加快执行时间.... 使用gcc -fverbose-asm -S进行优化和不优化编译并比较生成的汇编代码。 您在问为什么优化的代码运行得更快?这就是优化的重点。 DelayInCycles() 肯定是问题所在,可能是 for() 循环被优化掉了。请务必使用具有可预测频率的硬件计数器。还要确保所有设备寄存器都被声明为 volatile。 是的,答案将在汇编程序中。但猜测一下,优化器至少会执行函数内联。您绝对应该使用计时器功能(或类似功能)来限制您对 SPI 寄存器进行 bit-bash 的速率。 【参考方案1】:

对于每个命令和数据字节,您的代码调用两个函数,并且这两个函数都没有局部变量或许多临时变量。

当忠实地实现时,这些函数中的每一个都将创建一个堆栈帧(需要一些指令来设置和拆除)来存储任何不能保存在寄存器中的局部变量和临时变量。这大概就是-O0编译模式下会发生的事情。

可能影响此类代码执行时间的两个重要优化是:

堆栈帧遗漏:编译器注意到Write(可能还有WriteCommandWriteData)的堆栈帧未使用,并决定消除设置(和拆除)堆栈帧的指令.

函数内联:由于WriteWriteCommandWriteData 都是非常简单的函数,编译器可以决定完全消除函数调用并像您编写的那样生成代码(忽略任何可访问性问题):


    SPI m_spiPort();
    m_spiPort.Init();

    m_spiPort.LPC_SSP->DR = (SLEEPOUT & ~0x100);

    // Color Interface Pixel Format (command 0x3A)
    m_spiPort.LPC_SSP->DR = (COLMOD & ~0x100);
    m_spiPort.LPC_SSP->DR = (0x03 & 0x100);

    // Memory access controller (command 0x36)
    m_spiPort.LPC_SSP->DR = (MADCTL & ~0x100);
    m_spiPort.LPC_SSP->DR = (0x00 & 0x100);

    // Write contrast (command 0x25)
    m_spiPort.LPC_SSP->DR = (SETCON & ~0x100);
    m_spiPort.LPC_SSP->DR = (0x39 & 0x100);

    // Display On (command 0x29)
    m_spiPort.LPC_SSP->DR = (DISPON & ~0x100);

两种优化都消除了实际写入寄存器之间的大量(簿记)指令,从而使写入速度更快。

【讨论】:

以上是关于编译器优化如何加快简单操作之间的时间?的主要内容,如果未能解决你的问题,请参考以下文章

如何在 std::atomic<T> 上实现一个简单的自旋锁,以便编译器不会对其进行优化?

优化编译器如何决定何时展开循环以及展开循环的程度?

线程之间如何调用变量

C ++编译器优化和短路评估[重复]

linux编译c 优化,Linux性能优化——CPU性能调优

php 编译代码