vTaskDelay(pdMS_TO_TICKS(1000)); vs vTaskDelay(1000 / portTICK_PERIOD_MS);

mikemoy
Posts: 627
Joined: Fri Jan 12, 2018 9:10 pm

vTaskDelay(pdMS_TO_TICKS(1000)); vs vTaskDelay(1000 / portTICK_PERIOD_MS);

Postby mikemoy » Mon Jul 08, 2019 10:35 am

So i've been seeing more examples using vTaskDelay(pdMS_TO_TICKS(x)); over the IMHO traditional vTaskDelay(x / portTICK_PERIOD_MS);

I am just curious why this is. For when I look them up they are defines as such.

Code: Select all

//#define pdMS_TO_TICKS( xTimeInMs ) ( ( ( TickType_t ) ( xTimeInMs ) * configTICK_RATE_HZ ) / ( TickType_t ) 1000 )
vTaskDelay(pdMS_TO_TICKS(1000));
		
//#define portTICK_PERIOD_MS			( ( TickType_t ) 1000 / configTICK_RATE_HZ )
vTaskDelay(1000 / portTICK_PERIOD_MS);

Doesn't seem right to use vTaskDelay(pdMS_TO_TICKS(x)); when it gives same result but just adds more code using it.

WiFive
Posts: 3529
Joined: Tue Dec 01, 2015 7:35 am

Re: vTaskDelay(pdMS_TO_TICKS(1000)); vs vTaskDelay(1000 / portTICK_PERIOD_MS);

Postby WiFive » Mon Jul 08, 2019 10:16 pm

Is it more code?

Code: Select all

vTaskDelay(pdMS_TO_TICKS(1000)); -> ( ( ( TickType_t ) ( 1000 ) * configTICK_RATE_HZ ) / ( TickType_t ) 1000 )

vTaskDelay(1000 / portTICK_PERIOD_MS); -> (1000) / ( ( TickType_t ) 1000 / configTICK_RATE_HZ )
You have 2 divides vs 1 multiply and 1 divide. And if it is a constant it should be evaluated at compile time. If it is not a constant are you saying the compiler won't simplify the pdMS_TO_TICKS to one arithmetic operation?

mikemoy
Posts: 627
Joined: Fri Jan 12, 2018 9:10 pm

Re: vTaskDelay(pdMS_TO_TICKS(1000)); vs vTaskDelay(1000 / portTICK_PERIOD_MS);

Postby mikemoy » Mon Jul 08, 2019 10:59 pm

Wow, mental note. Dont be so hasty to look at something before making a post otherwise risk looking like a fool.
Well... Shaking the dust off my pants and getting back up. Still makes me wonder. Whats the point in invent something new that does the same thing with no gain.

Or so I thought Until i did this.

Code: Select all

for(uint8_t x = 0 ; x < 20 ; x++)
{
		
}
------------------- Memory utilization report -------------------
Used DATA_FLASH: 18KB out of 8192KB (0%)
Used INSTR_FLASH: 80KB out of 3264KB (2%)
Used INSTR_RAM: 37KB out of 128KB (29%)
Used DATA_RAM: 10KB out of 320KB (3%)

ok, now lets add vTaskDelay(pdMS_TO_TICKS(x)); to that loop to see the results.
------------------- Memory utilization report -------------------
Used DATA_FLASH: 18KB out of 8192KB (0%)
Used INSTR_FLASH: 80KB out of 3264KB (2%) [+24]
Used INSTR_RAM: 37KB out of 128KB (29%)
Used DATA_RAM: 10KB out of 320KB (3%)

Now lets replace that with vTaskDelay(x/ portTICK_PERIOD_MS);
------------------- Memory utilization report -------------------
Used DATA_FLASH: 18KB out of 8192KB (0%)
Used INSTR_FLASH: 80KB out of 3264KB (2%) [+32]
Used INSTR_RAM: 37KB out of 128KB (29%)
Used DATA_RAM: 10KB out of 320KB (3%)

So, in conclusion using vTaskDelay(x/ portTICK_PERIOD_MS) add more code.

WiFive
Posts: 3529
Joined: Tue Dec 01, 2015 7:35 am

Re: vTaskDelay(pdMS_TO_TICKS(1000)); vs vTaskDelay(1000 / portTICK_PERIOD_MS);

Postby WiFive » Tue Jul 09, 2019 2:31 am

So what are the extra two instruction words?

ESP_Angus
Posts: 2344
Joined: Sun May 08, 2016 4:11 am

Re: vTaskDelay(pdMS_TO_TICKS(1000)); vs vTaskDelay(1000 / portTICK_PERIOD_MS);

Postby ESP_Angus » Tue Jul 09, 2019 3:56 am

I think the only compelling reason is that the macro does the multiplication first and can be used with tick rates higher than 1000Hz without a division by zero error. (Although ESP-IDF doesn't support tick rates higher than 1000Hz.) For the same reason it may also be more accurate for some combinations of tick rate and delay in milliseconds.

Some discussion on the FreeRTOS list about this:
https://sourceforge.net/p/freertos/disc ... 637e/#d8a2

Was curious about the results you saw so I compiled these two functions (with default 100Hz tick time) and then disassembled them with "xtensa-esp32-elf-objdump -S":

Code: Select all

void delay_using_division(unsigned ms)
{
    vTaskDelay(ms / portTICK_PERIOD_MS);
}

void delay_using_macro(unsigned ms)
{
    vTaskDelay(pdMS_TO_TICKS(ms));
}

Code: Select all

00000000 <delay_using_division>:
   0:   004136          entry   a1, 32
   3:   000081          l32r    a8, fffc0004 <delay_using_division+0xfffc0004>
   6:   a2a280          muluh   a10, a2, a8
   9:   41a3a0          srli    a10, a10, 3
   c:   000081          l32r    a8, fffc000c <delay_using_division+0xfffc000c>
   f:   0008e0          callx8  a8
  12:   f01d            retw.n

Disassembly of section .text.delay_using_macro:

00000000 <delay_using_macro>:
   0:   004136          entry   a1, 32
   3:   a02220          addx4   a2, a2, a2
   6:   a02220          addx4   a2, a2, a2
   9:   11a2e0          slli    a10, a2, 2
   c:   000021          l32r    a2, fffc000c <delay_using_macro+0xfffc000c>
   f:   a2aa20          muluh   a10, a10, a2
  12:   41a6a0          srli    a10, a10, 6
  15:   000081          l32r    a8, fffc0018 <delay_using_macro+0xfffc0018>
  18:   0008e0          callx8  a8
  1b:   f01d            retw.n
Division method is, as predicted, smaller and simpler instruction code as it's division by a constant - the (configTICK_RATE_HZ / 1000) part is computed at compile time.

If the delay in ms is constant at compile time then everything is evaluated at compile time so the two methods would be equivalent in code size (but maybe not with identical result, depending on the various factors.)

Regarding the unusual results you saw: Small changes in code can sometimes have unexpected small results for binary size, due to padding and also due to link-time relaxation of literals.

WiFive
Posts: 3529
Joined: Tue Dec 01, 2015 7:35 am

Re: vTaskDelay(pdMS_TO_TICKS(1000)); vs vTaskDelay(1000 / portTICK_PERIOD_MS);

Postby WiFive » Tue Jul 09, 2019 5:07 am

Code: Select all


   3:   a02220          addx4   a2, a2, a2    
   6:   a02220          addx4   a2, a2, a2    
   9:   11a2e0          slli    a10, a2, 2         
compiler math for *100

Who is online

Users browsing this forum: Google [Bot] and 157 guests