• AVR Freaks

Hot!Ways to optimise C code for smaller program size?

Page: 12 > Showing page 1 of 2
Author
sjb741
Super Member
  • Total Posts : 834
  • Reward points : 0
  • Joined: 2010/01/25 08:45:39
  • Location: 0
  • Status: offline
2020/04/03 01:33:33 (permalink)
0

Ways to optimise C code for smaller program size?

I was hoping to get a breakdown of "program memory usage by function". I now realise that is not possible.
 
I think one reason is the compiler can, for example, merge common bits of code from different functions and thus a 'function' does not necessarily exclusively 'own' all it's program-memory code (?)
 
As an example - I have a project which used 'switch/case' to process 3 rows from a key-pad. Now the key pad only needs 2 rows, I can replace the 'switch/case' with 'if-else'.
 
Changing a 2-condition switch to if-else moved PIC16F1824 from code side 3911 to 3895 (saved 16 bytes).
 
But this is 'small beer', and I wondered what rules of thumb etc I could use to make more serios inroads to code size.
 
(Of course, there are various optimisation levels - but it is the C code itself I am trying to optimise in this case).
post edited by sjb741 - 2020/04/03 01:35:59
#1
ric
Super Member
  • Total Posts : 28390
  • Reward points : 0
  • Joined: 2003/11/07 12:41:26
  • Location: Australia, Melbourne
  • Status: online
Re: Ways to optimise C code for smaller program size? 2020/04/03 01:46:40 (permalink)
+1 (1)
Have you been examining the ".lst" file output by the compiler, which shows how it coded each function?
That is a very useful tool for seeing what is good, and bad, in your source.
 

I also post at: PicForum
Links to useful PIC information: http://picforum.ric323.co...opic.php?f=59&t=15
NEW USERS: Posting images, links and code - workaround for restrictions.
To get a useful answer, always state which PIC you are using!
#2
pcbbc
Super Member
  • Total Posts : 1703
  • Reward points : 0
  • Joined: 2014/03/27 07:04:41
  • Location: 0
  • Status: offline
Re: Ways to optimise C code for smaller program size? 2020/04/03 03:23:45 (permalink)
+2 (2)
Some quick wins...
* Avoid floating point maths routines.  Use integer/fixed point if at all possible.
* Check all your variable sizes.  Use the smallest types possible.  This will save RAM and also ROM if you can reduce the number/size of library routines pulled in.  Athough note that introducing a new type without eliminating one might actually increase code.
 
However by far the easiest options (assuming this isn't an existing product that must remain unchanged) are either:
a) Licence the compiler to get the best optimisation level (if you haven't already).
b) Switch to to a device with more resources.
#3
sjb741
Super Member
  • Total Posts : 834
  • Reward points : 0
  • Joined: 2010/01/25 08:45:39
  • Location: 0
  • Status: offline
Re: Ways to optimise C code for smaller program size? 2020/04/03 03:41:36 (permalink)
0
Thanks to both: I will look at the .lst file for a start.
 
By commenting out all but the last line of the function below, I saved 400 bytes. It is not a viable plan as I need the code. I am surprised that 400 bytes are used to encode the list of assignments.
 
This is only initialisation code. Maybe I should use C code which creates intialised structures?
 
void CreatePwmStructs(void) //Uses about 400 bytes of program code!
{
//PWM A on PWM3, TMR4
   gPwmA.ccpModuleId = 3;
   gPwmA.tmrId = 4;
   
   gPwmA.pTris = &TRISA;
   gPwmA.trisBit = 2;
   gPwmA.pCCPxCON = &CCP3CONbits;
   gPwmA.TxCON_BANK = 8; //Bank for associated Timer
   
   gPwmA.pTxCON = &T4CONbits;
   gPwmA.pTMRx = &TMR4;
   gPwmA.pCCPRxL = &CCPR3L;
   gPwmA.pPRx = &PR4;
   
   gPwmA.period = 40;
   gPwmA.duty = 20;
   gPwmA.dutyFine = 0;
   
   gPwmA.scaling = T246_PRE_1;
   gPwmA.phase = 0;
//PWM B on PWM4, TMR6
   gPwmB.ccpModuleId = 4;
   gPwmB.tmrId = 6;
   
   gPwmB.pTris = &TRISC;
   gPwmB.trisBit = 1;
   gPwmB.pCCPxCON = &CCP4CONbits;
   gPwmB.TxCON_BANK = 8; //Bank for associated Timer
   
   gPwmB.pTxCON = &T6CONbits;
   gPwmB.pTMRx = &TMR6;
   gPwmB.pCCPRxL = &CCPR4L;
   gPwmB.pPRx = &PR6;
   
   gPwmB.period = 40;
   gPwmB.duty = 20;
   gPwmB.dutyFine = 0;
   
   gPwmB.scaling = T246_PRE_1;
   gPwmB.phase = 20;
}//CreatePwmStructs

 
It saves a little bit of program space to use an initialisation style, about 25 bytes per structure. (I'm disappointed, as I thought it might be a big win...)
 
Tpwm gPwmB;

//NOTE: Saves some program space if I use an initialised structure rather than setting fields by assignment
//About 200 bytes used per structure initialisation, down to about 175 if I do it this way
//That is, if I do "gPwmA" this way, but leave "gPwmB" to code initialisation, 25 bytes of code space saved.
//PWM A on PWM3, TMR4
Tpwm gPwmA =
{
  .ccpModuleId = 3,
  .tmrId = 4,
  
  .pTris = &TRISA,
  .trisBit = 2,
  .pCCPxCON = &CCP3CONbits,
  .TxCON_BANK = 8, //Bank for associated Timer
  
  .pTxCON = &T4CONbits,
  .pTMRx = &TMR4,
  .pCCPRxL = &CCPR3L,
  .pPRx = &PR4,
  
  .period = 40,
  .duty = 20,
  .dutyFine = 0,
  
  .scaling = T246_PRE_1,
  .phase = 0
};

 
post edited by sjb741 - 2020/04/03 04:07:17
#4
crosland
Super Member
  • Total Posts : 2041
  • Reward points : 0
  • Joined: 2005/05/10 10:55:05
  • Location: Warks, UK
  • Status: offline
Re: Ways to optimise C code for smaller program size? 2020/04/03 03:45:16 (permalink)
0
How much benefit do you typically see form the licensed version compared to what -02 gives in 2.10?
 
#5
pcbbc
Super Member
  • Total Posts : 1703
  • Reward points : 0
  • Joined: 2014/03/27 07:04:41
  • Location: 0
  • Status: offline
Re: Ways to optimise C code for smaller program size? 2020/04/03 04:02:46 (permalink)
+3 (3)
Having pointers to all those registers doesn't help.  Accessing register via a pointer, over a static register reference, has an overhead of at least 4 instructions per access to retrive the pointer and move it to a FSR register.
 
Are these pointers dynamically modified by the code at runtime, or read only once you've completed the call to CreatePwmStructs?
 
Even if you need to modify them dynamically, you might be better of using a case or if statement and a direct reference to the register.  Depends how many references you have.
#6
andersm
Super Member
  • Total Posts : 2840
  • Reward points : 0
  • Joined: 2012/10/07 14:57:44
  • Location: 0
  • Status: offline
Re: Ways to optimise C code for smaller program size? 2020/04/03 05:52:59 (permalink)
+1 (1)
Try using const data as much as possible, then you don't need any initialization code at all. The compiler can't initialize something like this at compile time:

.pTris = &TRISA,

...but if you use the address directly, it can

.pTris = (volatile char*)(0xwhatever), // Use a macro for clarity

It looks like your struct contains both mutable and immutable data, but if you split it in two the const part can live fully in ROM.
#7
sjb741
Super Member
  • Total Posts : 834
  • Reward points : 0
  • Joined: 2010/01/25 08:45:39
  • Location: 0
  • Status: offline
Re: Ways to optimise C code for smaller program size? 2020/04/03 06:44:07 (permalink)
0
Well, potentially, yes the pointers could be altered in future code, future projects on another PIC having more PWMs, and this is really trying to make the code re-usable for that situation.
 
Try using const data as much as possible
 
That does look interesting, I will try that. I would have thought "&TRISA" was set in stone though?  

This is what I think is now indicated:
Tpwm gPwmA =
{
...  
    .tmrId       = 4,    
    .pTris = (volatile char*)(&TRISA),    
...
};
 
I am a little unsure why "volatile" helps in this situation though. My understanding is it indicates the contents can be altered by hardware (true enough). However, the header already says TRISA (itself) is volatile.
 
Note: I might not have done this right, but the following made no difference to code size
.pTris       = (volatile char*)(0x08C),//(&TRISA),  //Tried both styles
 
The above "lack of difference" applies whether of not I set "Address qualifiers" to "require" or "Ignore".
 
However, even though both initialistaion styles I tried made no difference to code size, I found that code size went down when I set "Address qualifiers" to "require" (for both styles).
post edited by sjb741 - 2020/04/03 07:00:38
#8
pcbbc
Super Member
  • Total Posts : 1703
  • Reward points : 0
  • Joined: 2014/03/27 07:04:41
  • Location: 0
  • Status: offline
Re: Ways to optimise C code for smaller program size? 2020/04/03 07:01:20 (permalink)
0
sjb741I am a little unsure why "volatile" helps in this situation though. My understanding is it indicates the contents can be altered by hardware (true enough). However, the header already says TRISA (itself) is volatile.
 
Note: I might not have done this right, but the following made no difference to code size
.pTris       = (volatile char*)(0x08C),//(&TRISA),  //Tried both styles



Nope, I didn't understand andersm's post either.  Both of these should result in the same code as far as I can see.  I can't see why the compiler would treat either expression differently.
 
Making your code future proof is all well and good, but when it inhibits your ability to fit the code in your current device it is perhaps time to re-think.
#9
NorthGuy
Super Member
  • Total Posts : 6296
  • Reward points : 0
  • Joined: 2014/02/23 14:23:23
  • Location: Northern Canada
  • Status: offline
Re: Ways to optimise C code for smaller program size? 2020/04/03 07:03:20 (permalink)
+3 (3)
IMHO, you can get the best gains by optimizing (simplifying) on the global level - using the data structures which makes operations easier, using algorithms which minimize operations, etc.
 
One of old programmers said: amateurs ignore complexity, professionals deal with complexity, geniuses remove complexity. Unfortunately, modern programming approaches went in the opposite direction - they add complexity. Instead, try to be a genius.
#10
du00000001
Just Some Member
  • Total Posts : 3946
  • Reward points : 0
  • Joined: 2016/05/03 13:52:42
  • Location: Germany
  • Status: offline
Re: Ways to optimise C code for smaller program size? 2020/04/03 07:03:28 (permalink)
+1 (1)
I'm cutting in late  :(
 
@ sjb741
 
Remember:
  1. All these initialization values have to be stored in Flash.
  2. The initializations have to be executed in some way whatever your code is.
  3. You've got 30 initialization lines in he code shown. 30 bytes less would equal 1 byte per initialization line.
  4. Ordering your struct's members in ascending order (wherever feasible) might result in bigger optimization gains. Even declaring and initializing "address gaps" (where applicable) could help.
To be honest: the above is from a "bad guy" who's been digging into optimizations for the last 25+ years reeatedly. I won't discuss about the "why"s here...

PEBKAC / EBKAC / POBCAK / PICNIC (eventually see en.wikipedia.org)
#11
sjb741
Super Member
  • Total Posts : 834
  • Reward points : 0
  • Joined: 2010/01/25 08:45:39
  • Location: 0
  • Status: offline
Re: Ways to optimise C code for smaller program size? 2020/04/03 08:41:19 (permalink)
0
When I set "Address qualifiers" to "require", code size drops by 151 bytes.

I have checked, and the code no longer uses 'bank setting' (in checking, I see I still have some struct variable "TxCON_BANK" but it is not used, so I can remove it).
 
Assuming I am right that my code makes no explicit use of bank selection, I am interested to see why the code size reduces when I force this option.
 
Note: When I removed the redundant struct field mentioned above, plus corresponding initialisation for 2 structures, code size fell by just two bytes.
post edited by sjb741 - 2020/04/03 08:45:28
#12
1and0
Access is Denied
  • Total Posts : 11143
  • Reward points : 0
  • Joined: 2007/05/06 12:03:20
  • Location: Harry's Gray Matter
  • Status: offline
Re: Ways to optimise C code for smaller program size? 2020/04/03 11:10:26 (permalink)
0
sjb741
When I set "Address qualifiers" to "require", code size drops by 151 bytes.


Bytes?!  I think it's words. ;)
 
#13
dan1138
Super Member
  • Total Posts : 3845
  • Reward points : 0
  • Joined: 2007/02/21 23:04:16
  • Location: 0
  • Status: offline
Re: Ways to optimise C code for smaller program size? 2020/04/03 13:08:55 (permalink)
+2 (2)
NorthGuy
IMHO, you can get the best gains by optimizing (simplifying) on the global level - using the data structures which makes operations easier, using algorithms which minimize operations, etc.
 
One of old programmers said: amateurs ignore complexity, professionals deal with complexity, geniuses remove complexity. Unfortunately, modern programming approaches went in the opposite direction - they add complexity. Instead, try to be a genius.

In my experience with resource limited 8-bit controllers and the C programming language almost everything done to use "best practices", modularity and abstractions cause the compiled code to bloat.
 
There are now controllers with architectures that C compilers can generate efficient code for. It is just that no Microchip 8-bit compiler is all that good at it. The Hi-Tech Omniscient-Code-Generator™ seemed like a good idea at the time but after 25 years on, the code generator for an 8-bit PIC it seems to be about as good as it gets.
 
On a good day I can hand code an assembly language implementation in maybe 5% less code than the XC8 pro mode does it. So under most reasonable, for profit, firmware project creation and maintenance cases it's not justified to use assembly language.
 
So I see it is useless to complain to Microchip about the quality of the code generated by XC8. Nothing has changed in that area since Microchip purchased Hi-Tech and effectively put them out of the controller tools business.
 
No competition no innovation.
post edited by dan1138 - 2020/04/03 13:36:00
#14
mlp
boots too small
  • Total Posts : 965
  • Reward points : 0
  • Joined: 2012/09/10 15:12:07
  • Location: previously Microchip XC8 team
  • Status: offline
Re: Ways to optimise C code for smaller program size? 2020/04/03 15:12:15 (permalink)
+1 (1)
dan1138
In my experience with resource limited 8-bit controllers and the C programming language almost everything done to use "best practices", modularity and abstractions cause the compiled code to bloat.

Agree, 700%.
 
So I see it is useless to complain to Microchip about the quality of the code generated by XC8. Nothing has changed in that area since Microchip purchased Hi-Tech

Hey, I think you're being a bit harsh:
  • We first made use of the PIC18 hardware multiplier post-acquisition, although it has come to light that non-PRO licenses don't get full benefit from that.
  • We were able to influence some more-recent chip designs to be a less-crappy compiler target, although we lost some battles there too.
Incremental improvements.

Mark (this opinion available for hire)
#15
NKurzman
A Guy on the Net
  • Total Posts : 18909
  • Reward points : 0
  • Joined: 2008/01/16 19:33:48
  • Location: 0
  • Status: offline
Re: Ways to optimise C code for smaller program size? 2020/04/03 16:33:00 (permalink)
+1 (1)
I guess the bigger question is how small do you need the code to be. You’re asking some pretty specific optimization questions, not the typical General ones.
Does your code fit into the chip? Are you trying to shave off a specific amount?
Can you get a bigger one if you need to?
#16
andersm
Super Member
  • Total Posts : 2840
  • Reward points : 0
  • Joined: 2012/10/07 14:57:44
  • Location: 0
  • Status: offline
Re: Ways to optimise C code for smaller program size? 2020/04/04 00:54:04 (permalink)
0
pcbbcNope, I didn't understand andersm's post either.  Both of these should result in the same code as far as I can see.  I can't see why the compiler would treat either expression differently.

I was remembering a different case. IIRC GCC can't use symbols provided by the linker as constant expression initializers, but that doesn't apply here.
#17
Jan Audio
Senior Member
  • Total Posts : 159
  • Reward points : 0
  • Joined: 2018/09/24 08:12:24
  • Location: 0
  • Status: offline
Re: Ways to optimise C code for smaller program size? 2020/04/04 06:26:42 (permalink)
0
Use functions instead of macro`s.
#18
NorthGuy
Super Member
  • Total Posts : 6296
  • Reward points : 0
  • Joined: 2014/02/23 14:23:23
  • Location: Northern Canada
  • Status: offline
Re: Ways to optimise C code for smaller program size? 2020/04/04 06:58:23 (permalink)
0
andersm
pcbbcNope, I didn't understand andersm's post either.  Both of these should result in the same code as far as I can see.  I can't see why the compiler would treat either expression differently.

I was remembering a different case. IIRC GCC can't use symbols provided by the linker as constant expression initializers, but that doesn't apply here.



When the compiler doesn't know the address of a symbol, the compiler creates relocation records in the object file. Relocations describe the symbol and location where the address of this symbol needs to be. When the linker assembles the final executable, it goes through relocation records and resolve them. This way, the compiler can generate the code as if the symbol was known at the compile time. This is how it's been since I remember.
 
Of course there are some limitations. Not all relocation types may be supported. Compiler cannot do arithmetic operations (such as difference between two addresses). But the majority of the cases is covered.
 
#19
davea
Super Member
  • Total Posts : 316
  • Reward points : 0
  • Joined: 2016/01/28 13:12:13
  • Location: Tampa Bay FL USA
  • Status: offline
Re: Ways to optimise C code for smaller program size? 2020/04/04 11:29:52 (permalink)
0
there is one more to thing to look at
MOVLB in the disassembly listing if there lots of them 
see if you can use "bank" settings to place variables in the best location
use a few "near" bytes if it helps
put as many temp variables inside functions as possible
making sure they use the same bank 
in one case I was able to reduce MOVLB from 338 to 161
and code size 10%
XC8 mode 3
#20
Page: 12 > Showing page 1 of 2
Jump to:
© 2020 APG vNext Commercial Version 4.5