3

I compiled the following two sketches with the compiler used by the Arduino IDE and ran them on an Arduino UNO (smd edition). I'm using:

  • Arduino IDE v. 1.8.0
  • Arduino AVR Boards v. 1.6.20

1.

void setup() {

  Serial.begin(115200);
  unsigned long a, b;
  //unsigned long c, d;


  a = micros();
  for (unsigned long i = 0; i != 1000000; i++) digitalRead(13);
  b = micros();
  Serial.println(b - a);


  //c = micros();
  //for (unsigned long i = 0; i != 1000000; i++) digitalRead(13);
  //d = micros();
  //Serial.println(d - c);

}

void loop () {}

2.

void setup() {

  Serial.begin(115200);
  unsigned long a, b;
  unsigned long c, d;


  a = micros();
  for (unsigned long i = 0; i != 1000000; i++) digitalRead(13);
  b = micros();
  Serial.println(b - a);


  c = micros();
  for (unsigned long i = 0; i != 1000000; i++) digitalRead(13);
  d = micros();
  Serial.println(d - c);

}

void loop () {}

These were the outputs on the serial monitor:

  1. 2452320

  2. 3332640 3332704

Why did each of the two for loops in the second sketch take roughly 33% more time to execute than the loop in the first one?

5
  • I ran both sketchs on a Arduino Uno and got the same times ( 4275900us +/- 100) in both cases.
    – user31481
    Aug 30, 2017 at 17:06
  • If you retry both tests, you get the same numbers as above? Aug 30, 2017 at 18:24
  • @MichelKeijzers yes, I always get exactly the same numbers. Aug 30, 2017 at 21:59
  • It would be helpful if you edited your question to state which version of the Arduino IDE you're using and which version of Arduino AVR Boards (shown in Tools > Board > Boards Manager).
    – per1234
    Aug 30, 2017 at 22:08
  • 1
    @per1234 Arduino IDE 1.8.0, Arduino AVR Boards 1.6.20. I've just edited the question. Aug 30, 2017 at 22:20

1 Answer 1

1

The different time implies different (number of) instructions being executed. This is most probably due to the crappy GCC compiler optimizations.

In the former sketch, probably the compiler manages to use only registers, which are very fast. In the second one, the compiler THINKS it needs 8 more bytes, and there are no registers left, so it uses the stack (internal RAM) to store some variables, which is slower. (still, the optimizer should realize that in fact it don't need more bytes, because a and b are not used after the first println()). To access to a SRAM location, you need a LOAD/STORE operation, which takes a longer time, with respect to an operation between registers (beside having to load the memory address to X, Y o Z). Probably, the compiler is even crappier, because, instead of putting the rarely-used variables a,b,c,d on the stack, it puts i (which is always incremented) or other variables used in digitalRead().

Still, to explain where is the trouble (i.e. where compiler fails in optimizing the register allocation/usage), you should disassemble the object code. Use for instance Atmel Studio, which still uses GCC, but has a good debugger, disassembler and cycle-exact analysis.

Still, this is very strange, perhaps optimizations were not enabled the platform.txt of your arduino?

EDIT: Using Arduino 1.8.0, I tested the two sketches and analyzed the lss (generated using avr-objdump). The compiler optimizes a lot more the first code. Instead of making a CALL to digitalRead() (as it happens in the second sketch) it probably recognizes that such function is called only once on the code. Therefore it makes a rjmp to it. At the end of that function, there won't be a RET but a branch to the point where i is computerd and checked (actually i is not incremented, actually gets decremented and checked against 0).

In other words, even if the two codes are equal (except that the second sketch runs the same code twice), they are compiled (optimized) differently.

9
  • 2
    Sorry, but you are wrong: the generated assembler for both cases works using only registers and inmediate values. No memory access, no stack use. The code and registers used are the same for the common part of both sketchs. Have a look at the .lss generated file (I'm using UECIDE).
    – user31481
    Aug 30, 2017 at 17:25
  • Are you sure you are using the same setup of the OP?
    – next-hack
    Aug 30, 2017 at 17:44
  • Sure we have different setup! But I presented facts, you don't. Maybe I wrong (not the first time); just have look at your generated code, so we can have an useful analysis of platforms and compilers.
    – user31481
    Aug 30, 2017 at 18:01
  • Finally I did a test. Actually I was wrong. It's not that GCC does not optimize the second sketch. Instead, it optimize a lot the first one (in my case, I'm using Arduino 1.8.0). Instead of making a CALL to digitalRead, it probably recognizes that digitalRead is called only once in the code, therefore it makes a rjmp to digital read, then from digitalRead goes back to the for loop, where it computes and checks i. (I'll edit the question. Edits will be visible for on the edit history, so the comments will still make sense).
    – next-hack
    Aug 30, 2017 at 19:13
  • 1
    UECIDE's compilation methodology is somewhat different to Arduino's. It probably doesn't optimize in the same way because it compiles the different portions separately and archives them. Makes it harder for functions to be inlined or munged with rjmp instead of call. Also the compiler may be a different version (I haven't updated it in a while, maybe I'll check for a newer version today).
    – Majenko
    Aug 31, 2017 at 10:00

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.