首页 > 代码库 > 程序的位置与运行时间有关

程序的位置与运行时间有关

一、概述

  在我测量mian函数大循环的周期时,发生了怪事:我一点也没有更改main函数的代码,只在初始化时添加了一些无用的语句,结果测量时间发生了巨大的差异。

二、事件详情

1、测试代码

main函数

/**
  * @brief  主函数
  * @param  无
  * @retval 无
  */
int main(void)
{        
        LED_GPIO_Config();      
        /* USART1 config 115200 8-N-1 */
        USART1_Config();

     /**
      * @brief  通过注释掉程序,可以更改代码的长度
      * @param  当注释的只剩下一个赋值等式的时候,计时长度值是0x116638
      * @retval 当注释的剩下三个赋值等式的时候,计时长度值是0xEA726
      */
        DMA_USART1_Config();
        
        NVIC_Configuration(); 
        SysTick_Init();     
        SysTick_Time_Init(&UseDMATime); 
        for(;;)
        {     
            SysTick_Time_Start();
            LED1(ON);LED2(OFF);LED3(OFF);
            Delay();
            LED1(OFF);LED2(ON);LED3(OFF);
            Delay();
            LED1(OFF);LED2(OFF);LED3(ON);
            Delay();
            SysTick_Time_Stop();
        }
}

Delay函数

static void Delay(void)     //简单的延时函数
{     
    
        uint16_t i;
        
        /*填充将要发送的数据*/
        for(i = 0;i < SENDBUFF_SIZE ; i++)
        {
            SendBuff[i]     = (uint8_t)i;
        }
    
}

说明:

(1)测量代码段

            LED1(ON);LED2(OFF);LED3(OFF);
            Delay();
            LED1(OFF);LED2(ON);LED3(OFF);
            Delay();
            LED1(OFF);LED2(OFF);LED3(ON);
            Delay();

2)无效代码段

  之所以称之为无效,实际上指的是“这些代码在程序中起到的是很次要的作用,主要是给CPU一些空指令,当然也会起到改变代码量的作用”。

        LED_GPIO_Config();      
        /* USART1 config 115200 8-N-1 */
        USART1_Config();

     /**
      * @brief  通过注释掉程序,可以更改代码的长度
      * @param  当注释的只剩下一个赋值等式的时候,计时长度值是0x116638
      * @retval 当注释的剩下三个赋值等式的时候,计时长度值是0xEA726
      */
        DMA_USART1_Config();

(3)与测量计时有关的代码

SysTick_Init();     
SysTick_Time_Init(&UseDMATime); 
SysTick_Time_Start();
SysTick_Time_Stop();

  关于测量计时代码的实现在随笔STM32之系统滴答定时器中进行了详细讲解。

2、奇怪现象

  DMA_USART1_Config是在大循环之前调用的一个无用的函数,实际上可以称之为空函数或者延时函数。通过注释掉里边的语句可以改变排在DMA_USART1_Config代码段之后代码在Flash上的位置,也可以改变整个代码段的量(尽管很小)。

void DMA_USART1_Config(void)
{
        DMA_InitTypeDef DMA_InitStructure;
        
        DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;    

        /*传输大小DMA_BufferSize=SENDBUFF_SIZE*/    
        DMA_InitStructure.DMA_BufferSize = SENDBUFF_SIZE;
        
        /*外设地址不增*/        
        DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; 

 
} 

  笔者发现,当这个函数完全保留三行语句的时候,while循环测出来的滴答计时器脉冲数是0xEA726(0d960294);而当注释掉只剩下一行语句时,脉冲数变为0x116638(0d1140280)。这中间的差距是0d179986,占0xEA726的百分比是18.7%。也就是说注释语句后的代码执行时间,比没有注释时的时间多了将近1/5,这值得考虑其中的原因。

三、分析与结论

1、分析

     测量代码段中有关LED操作的代码执行时间几乎可以忽略,而占主要运行时间的是Delay函数,只要分析Delay函数,就能找到其中的问题。

Delay函数

static void Delay(void)     //简单的延时函数
{     
    
        uint16_t i;
        
        /*填充将要发送的数据*/
        for(i = 0;i < SENDBUFF_SIZE ; i++)
        {
            SendBuff[i]     = (uint8_t)i;
        }
    
}

Delay函数在两种情况下的反汇编代码

在完全保留代码的情况下

在注释掉部分代码的情况下

   可以看到Delay函数的反汇编代码内容没有改变,改变的只是代码在Flash中的位置(当然也是程序执行时的位置)。

      而后,我有试着在

DMA_USART1_Config代码的基础(3行语句)上不断增加代码,发现计时脉冲数周期性的在0xEA726、0x116638两个中变换。

       由此可以确定,就是由于代码的位置改变了程序的运行时间,也即改变了程序的运行速度。

2、结论

  即使是同样的代码,通过改变代码的位置可以改变代码的运行时间。而最根本的原因是有些指令的周期给指令所在的Flash中的位置有关。

3、还未完成的工作

  但是,由于J-link硬件调试时,单步调试误差太大,取得的值很有出入,难以确定究竟是Delay函数中哪些指令引起时间偏差。