hidden gem

Go To Last Post
71 posts / 0 new

Pages

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I think the phrase "will generate code as good as an Asm programmer" is usually with something like "about 90% of the time". Sure there are occasions where it's suboptimal but the positives outweigh the negatives.

 

As a simple exercise write a JPEG encoder. Your choice: C or Asm.

 

Report back in 2 weeks ;-)

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

clawson wrote:

I think the phrase "will generate code as good as an Asm programmer" is usually with something like "about 90% of the time". Sure there are occasions where it's suboptimal but the positives outweigh the negatives.

 

Actually the positives _far_ outweigh the negatives, I'm not arguing about that. Just saying, that if the compiler screws up 10% of the time, and 10% of the code is time critical, there is a 1% chance that the compiler will screw up time critical code. It happens. So I like to check the quality of generated code in these parts just to be sure.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 1

avrcandies wrote:
I don't think GCC would be able to determine which variables are most critical, in terms of impacting efficiency---or does it?

 

In 9 cases out of 10 GCC will much better than the user at determining it.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

avrcandies wrote:

Is there a way to tell GCC that, say RPM, is an important 16 bit value (in a motor controller app) & should be kept in a register pair, rather than delegated to sram with "lower-importance" values.

 

I believe there is a keyword modifier 'register' that you can apply to variables, but it would probably depend on the compiler.  S.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

clawson wrote:

I think the phrase "will generate code as good as an Asm programmer" is usually with something like "about 90% of the time". Sure there are occasions where it's suboptimal but the positives outweigh the negatives.

 

The problem I have there is that you don't know when it's being weird, without inspecting the asm code, which isn't that far off writing it in the first place.  Freely granted, most of the time it doesn't matter, but when it does, you might want to know.

 

clawson wrote:

As a simple exercise write a JPEG encoder. Your choice: C or Asm.

 

Report back in 2 weeks ;-)

 

My AVRs don't do that.  They interact with external hardware, not pushing pixels around!  If you want to push pixels, get a Rasp Pi or something, and yes, use C to your heart's content therein.

 

It's very rarely raw speed (is if you're calculating Mersenne primes, but that's an oddball case), it's much more frequently needing precise timing to interact with the other hardware.  Yes, it would be nice to have optimized code in the user interface.  That is absolutely not worth it if the compiler optimizes out the 'nop's that provide the precision timing!  (Oh, I'm sure there's ways to force the compiler to behave itself, but why bother fighting it?)

 

I like to write down not what I want the chip to do, but exactly what the chip is going to do.  So I cut out the middleman.  Call me a Luddite if you want to.  I write my AVR code in assembler.  So there.  ;-P   S.

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 1

Scroungre wrote:
I believe there is a keyword modifier 'register' that you can apply to variables, but it would probably depend on the compiler

The keyword is part of the language standard.

 

What - if any - effect it has will depend upon the particular compiler.

 

I think it is largely ignored by modern compilers ... ?

Top Tips:

  1. How to properly post source code - see: https://www.avrfreaks.net/comment... - also how to properly include images/pictures
  2. "Garbage" characters on a serial terminal are (almost?) invariably due to wrong baud rate - see: https://learn.sparkfun.com/tutorials/serial-communication
  3. Wrong baud rate is usually due to not running at the speed you thought; check by blinking a LED to see if you get the speed you expected
  4. Difference between a crystal, and a crystal oscillatorhttps://www.avrfreaks.net/comment...
  5. When your question is resolved, mark the solution: https://www.avrfreaks.net/comment...
  6. Beginner's "Getting Started" tips: https://www.avrfreaks.net/comment...
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 1

clawson wrote:
code as good as an Asm programmer

Does, of course, depend upon how good the particular "Asm programmer" is.

Top Tips:

  1. How to properly post source code - see: https://www.avrfreaks.net/comment... - also how to properly include images/pictures
  2. "Garbage" characters on a serial terminal are (almost?) invariably due to wrong baud rate - see: https://learn.sparkfun.com/tutorials/serial-communication
  3. Wrong baud rate is usually due to not running at the speed you thought; check by blinking a LED to see if you get the speed you expected
  4. Difference between a crystal, and a crystal oscillatorhttps://www.avrfreaks.net/comment...
  5. When your question is resolved, mark the solution: https://www.avrfreaks.net/comment...
  6. Beginner's "Getting Started" tips: https://www.avrfreaks.net/comment...
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

awneil wrote:

I think it is largely ignored by modern compilers ... ?

 

Beautiful.  You think it might be, or it might not.  The compiler might care, or it might not.  It might do what you want it to do... or it might not.

 

When you write assembler, you know.  S.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 1

Scroungre wrote:
When you write assembler, you know. 

And if you need to know, you should be writing in assembler anyhow.

 

Top Tips:

  1. How to properly post source code - see: https://www.avrfreaks.net/comment... - also how to properly include images/pictures
  2. "Garbage" characters on a serial terminal are (almost?) invariably due to wrong baud rate - see: https://learn.sparkfun.com/tutorials/serial-communication
  3. Wrong baud rate is usually due to not running at the speed you thought; check by blinking a LED to see if you get the speed you expected
  4. Difference between a crystal, and a crystal oscillatorhttps://www.avrfreaks.net/comment...
  5. When your question is resolved, mark the solution: https://www.avrfreaks.net/comment...
  6. Beginner's "Getting Started" tips: https://www.avrfreaks.net/comment...
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Which I do.  Cheers to all of you!  a great gang, you is.  heart  S.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

>> I think ["register"] is largely ignored by modern compilers ... ?

Beautiful.  You think it might be, or it might not.  The compiler might care, or it might not.  It might do what you want it to do... or it might not.

"register" is quite old, but is largely obsolete because compilers have gotten much better at automatically putting variables in registers.  I don't know whether the declaration "bumps" the priority of a variable for register-ification in modern gcc, or whether it relies entirely on its own calculations.  (originally it was a "hint" that the variable would be frequently accessed.  Nowadays, the compiler figures out in detail which variables are used most...

 

gcc also has a global "register" variable definition, but it's not clear whether it works on variables larger than the cpu register size.  https://gcc.gnu.org/onlinedocs/g...

 

When you write assembler, you know.

Yes, of course.  you MUST know, and your MUST keep track, and you need to pay attention to which registers are usable for which purposes, and if you end up being wrong about overall performance then you have no one to blame but yourself.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

westfw wrote:
but it's not clear whether it works on variables larger than the cpu register size. 
It does. If you do something like:

register uint16_t foo asm("r16");

then it's assigned to r17:r16:

register uint16_t foo asm("r16");

int main(void)
{
	foo = 0x1234;
  7a:	04 e3       	ldi	r16, 0x34	; 52
  7c:	12 e1       	ldi	r17, 0x12	; 18

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

El Tangas wrote:

And even the most recent avr-gcc version I have, v9.1.0, still auto-generates a 16 bit counter to count to 8.

So, when people say "Just trust the compiler, it will generate better code than the average programmer would hand code in assembly"... err, no. Maybe it will most of the time, but then mess up a time critical loop. I always double check important parts of the generated code.

 

Um... Are you sure you are not mixing "time-critical" with "space-critical"? GCC will often prefer to use registers whose bit-width matches the natural "bitness" of the platform (i.e. 16-bit registers on a 16-bit platform) because such registers are simply faster. GCC treats your loop as time-critical and that is exactly why it opts for a more efficient 16-bit register (instead of an 8-bit register).

 

P.S. This is the same reason why every time one sees "small" integer types (like uint8_t being used) as local arithmetic variables in a C program (just because 8 bits was "enough"), one immediately realizes that the code is written by an amateur. In properly written C programs local integer arithmetic is always done in "natural" types signed/unsigned int as much as possible, even if their range is excessive. This will produce the most time-efficient code.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

AndreyT wrote:
GCC treats your loop as time-critical and that is exactly why it opts for a more efficient 16-bit register (instead of an 8-bit register)

I fail to see how a 16bit counter is more efficient than 8bit on an 8bit micro.

The assembler produced by the compiler for this partiular code snippet is clearly not optimal for this architecture.

Personally, I think so what, it's not going to stop me using C.

But the fact remains it is in this case non optimal.

 

Sticking with 8bit micros, I'm not sure I agree with using 'int sized' variables, just because things are (conceptually at least) promoted to int before an operation (if that's what you are saying, I may have misunderstood).

Given

uint8_t a, b, c;

a = b + c

Although b + c is conceptually performed as int (16 bit), the compiler knows that since the result is assigned to a uint8_t it only needs to do an 8bit add.

(I'm assuming that you know the result will always fit in 8 bits).

 

If you had

int a;

uint8_t b, c;

then for a = b + c  it would have to do 16bit add.

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

AndreyT wrote:
one immediately realizes that the code is written by an amateur.
Yeah but often the iterator type is picked to be documentary. If I see a uint8_t in a for() loop I know the author never intends the counting range to exceed 255. I wouldn't call such hints the maintainer "amateurish" myself. (code isn't always about being fast / small). 

 

MISRA (fairly professional) forbid the use of "signed int"/"unsigned int" and the other base types (apart from "char") anyway.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I shall grant at first that this particular field is 'general programming', but the site itself is called AVRFreaks, so we're probably mostly hanging around on 8-bitters?  Maybe?  There's a few 32 among us, and yes, some AVR registers can be paired to be addressed sixteen-bit-ly, as it were, but mostly speaking:

 

AndreyT wrote:

Um... Are you sure you are not mixing "time-critical" with "space-critical"? GCC will often prefer to use registers whose bit-width matches the natural "bitness" of the platform (i.e. 16-bit registers on a 16-bit platform) because such registers are simply faster. GCC treats your loop as time-critical and that is exactly why it opts for a more efficient 16-bit register (instead of an 8-bit register).

 

The 'bitness' of the AVR processor is eight bits, not sixteen.  The 'bitness' of the larger processors is 32, not 16.  If you're really whacking away on the big iron, the 'bitness' is 64.  Not sixteen. 

 

's true there are other Microchip products that are sixteen bits wide, and you're right, it is best to adjust your code to the hardware it gets to run on.  I've built systems from four bits to eighteen (yes, that one was a bit weird), forty, and beyond, and the best code is that which pays attention to the hardware.  Know thy targets.  S.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

On AVRs, and I expect other microcontrollers, gcc allows one to use inline assembly on local variables.

One need not write the entire function in assembly and

can still get the timing right by counting the times of instructions.

'Tis my understanding that this is not true of other compilers or other processors:

Other compilers do not have gcc's syntax for referring to local variables and

larger processors have more complicated timings.

Counterexamples?

Iluvatar is the better part of Valar.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I would avoid this construction (x-->0) because the symbols are too close to the syntax used for accessing a structure member when using a pointer to the structure: x->0:  which may refer to the beginning of the structure (member number 0 of the structure pointed to by x).

 

An interesting gem is C's ability to "reverse" the syntax of an array.  So  myArray[5] is the same as 5[myArray].  The compiler starts at 5 times the size of the array element and then adds the beginning address of myArray[0] to get the final address 5[myArray].

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Simonetta wrote:
x->0:  which may refer to the beginning of the structure (member number 0 of the structure pointed to by x).

Nonsense - structure members are not referred to by number!

Top Tips:

  1. How to properly post source code - see: https://www.avrfreaks.net/comment... - also how to properly include images/pictures
  2. "Garbage" characters on a serial terminal are (almost?) invariably due to wrong baud rate - see: https://learn.sparkfun.com/tutorials/serial-communication
  3. Wrong baud rate is usually due to not running at the speed you thought; check by blinking a LED to see if you get the speed you expected
  4. Difference between a crystal, and a crystal oscillatorhttps://www.avrfreaks.net/comment...
  5. When your question is resolved, mark the solution: https://www.avrfreaks.net/comment...
  6. Beginner's "Getting Started" tips: https://www.avrfreaks.net/comment...
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Simonetta wrote:

An interesting gem is C's ability to "reverse" the syntax of an array.  So  myArray[5] is the same as 5[myArray].  The compiler starts at 5 times the size of the array element and then adds the beginning address of myArray[0] to get the final address 5[myArray].

I see what you're saying, but I think it's more easily understood by recognising that, under the hood, array subscripting is implemented as pointer arithmetic.

So myArray[5] becomes *(myArray + 5)

and 5[myArray] becomes *(5 + myArray)

 

The end result is the same either way, it's just adding an integer to a pointer (pointer arithmetic) and then dereferencing the resulting address.

Pages