-
-
Notifications
You must be signed in to change notification settings - Fork 1.1k
analogRead - reading ADC result faster #344
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Comments
Would be nicer if the compiler would just optimize it. I wonder if the volatile nature of these variables somehow prevent this... One thing to consider: Is this register protected against partial reads? Some registers require that you first read the lower half and then the upper (or the other way around, dunno) to prevent updates while reading. If so, using separate registers might have more guarantees (though maybe only when both are read in two different statements, dunno what guarantees volatiles give exactly). |
Well, in analogRead variables "high" and "low" are not declared as a volatile. But the compiler doesn't optimize the equation (high << 8) | low anyway. You can make sure of it by disassembling the output .elf file. And yeah, ADCL must be read first, but the compiler deals with it good when using macros _SFR_MEM16(0x78). You can see it from the code above, first instruction is LDS R24,0x0078 which loads in R24 data stored in register with address 0x78 (which is the ADCL in case of atmega328). All needed defines for each avr mc have already been provided by avr/io library. So only wiring_analog.c should be changed. |
Yeah, they are.
See https://www.nongnu.org/avr-libc/user-manual/sfr__defs_8h_source.html
Yeah, it does it correctly in this particular compilation, but I' wondering if it makes any guarantees (I suspect not). If not, it might end up loading them in a different order on another sketch, or a future compiler version maybe. Though maybe the language does not make any guarantees, but |
Oh, I'm afraid we were talking about different variables. Macros like
I have investigated and found out that gcc has had feature of proper reading and writing access order to 16-bit registers for AVR-architecture since 2005. Here is the topic related with this fix. According to the ChangeLog:
In file avr.c in the definition of the function
I have not dug deeper, but I think, it's clear that as long as the pointer to 16-bit register declared as volatile there would be proper order of reading. |
Hm, thanks for digging up that bug report. It does seem that gcc intentionally gets the ordering right, but it also clearly shows what I was afraid of: That the gcc folks are not really keen on having such exceptions in the codebase, so there seems a small but realistic risk of this guarantee being removed (or forgotten) at some point... Also, I wondered if we could not just get the optimizer to handle this correctly somehow. I tested with gcc 5.4.0 (Debian default), which indeed seems stubborn, but it seems that gcc 7.3.0 (used by current avr core) does get it right here, without any additional massaging (though with just a minimal example):
What compiler are you using for testing? |
I'm afraid that my first example was from atmel studio where there is gcc 5.4.0. But then i tested the output elf. files from arduino IDE with gcc 7.3.0 and this version actually does make it worse! See what happened with your example if you try to read this expression into the variable. Here is the code
And here is the output, see that part with tripple
Actually function like
I have compiled this sketch in arduino IDE 1.8.12 then disassembled output .elf file with following commands
Here is the piece of the output code.
As you can see between reading ADC values and putting them somewhere else there are three
|
Wow, that's quite horrible. I think the triple eor is a way to swap two registers without needing a third one, too bad that there is absolutely no need... Hm, so much for my optimism. And using the |
Yeah, with
So I have declared variable as volatile to make sure that compiler writes ADC value somewhere at the data area. Here is the output result with original wiring_analog.c.
And here it is with changed analogRead to
|
On my windows PC with current Arduino 1.8.13
geenrated with So what is the problem? Please close the issue and the PR. |
Hm, it might be that the sketch around it makes a big difference? I.e. depending on how you use the result of When I compile the
Which is slightly better than the original cases (no 3x eor), but still swaps registers using @ArminJo, I'm surprised that you're not seeing the same result. Also, in your case I see a When I compile the last example from @Vitve4, I get the same 3x
|
Sorry, yes you are right, I also had the same result with the original |
Well, it does seem to. And I can imagine it does, since using the Note that I think gcc should just be able to figure this out and optimize this, so in essence this is a missed optimization in gcc. But if using the |
I just reviewed the PR, looks good to me. In addition to fixing this here, it would still be good if gcc could fix this as well. Anyone care to look through the gcc bugzilla to see if this issue is maybe already reported and/or report it if not? And if anyone has a newer avr-gcc version to test, that would also be valuable. |
|
It's common for Arduino sketches to use a call to randomSeed(analogRead(A0)); That sketch may well have no other use of |
@ArminJo Do you see any downside for this change? You seem to be heavily against this, but I see only the argument that the gain would little, but if there is no cost, then why not make the change? Is it just the cost of making the change that you see, or is there something else? |
After forther testing and with per1234's argument in mind and sleeping on it I changed my mind and think it is a useful change! |
7.3.0-atmel3.6.1-arduino7 gcc fails to optimize separate reading from ADCL and ADCH. It produces additionally three eor commands or in some cases two mov commands in the assembly code (see discussion arduino#344). These commands swap register contents before store them to data area. So they are completely unnecessary. Reading ADC result with ADC macro fixes it and gcc generates the right code..
7.3.0-atmel3.6.1-arduino7 gcc fails to optimize separate reading from ADCL and ADCH. It produces additionally three eor commands or in some cases two mov commands in the assembly code (see discussion arduino#344). These commands swap register contents before store them to data area. So they are completely unnecessary. Reading ADC result with ADC macro fixes it and gcc generates the right code..
Fixed by #345 |
In current core adc result is read by combining two bytes ADCH and ADCL, that way adds two unnecessary commands in assembler code. But avr-gcc compiler provides a way to access 16-bit ADC register through ADC or ADCW as long as it is defined. avr/io libraries provide those defines.
It is shown below the difference in reading adc result as a combining two bytes or reading directly. As you can see there are two more command LDI and OR when combining two bytes, which are completely unnecessary.
I can take care of this fix, if it is needed.
The text was updated successfully, but these errors were encountered: