C runtime optimization - division and modulous together - Page 1
Forum section: electronics / beginners

author: aurmer
author_rank: Regular Contributor
timestamp: December 16, 2016, 07:56:51 pm
content: Hello fellow EEVers.I have a C 'fundamentals' issue that I am dealing with. I am working with Microchip's XC16 compiler (which I believe is JUST LIKE C30 but with a few architecture-specific optimizations).I know that / does the same machine operation as %.When you perform this operation, the / result is placed in w0 and the % result is placed in w1.My goal is runtime speed. In C, is there a way for me to perform this division operation ONCE and receive both the division result AND the remainder all with one C operation? My best idea is to take my two operands and use inline assembly to do division and then store w0 at address X and w1 at address Y and then proceed.Am I missing something obvious? Is there a better way?Thanks!

author: IanB
author_rank: Super Contributor
timestamp: December 16, 2016, 08:01:29 pm
content: Have you considered the div() function in the standard library?http://www.cplusplus.com/reference/cstdlib/div/

author: aurmer
author_rank: Regular Contributor
timestamp: December 16, 2016, 08:03:20 pm
content: Thank you for that point, I will test that at runtime and present my results here.

author: Twoflower
author_rank: Frequent Contributor
timestamp: December 16, 2016, 08:20:15 pm
content: Note: I don't know the quality of the XC16 compiler.Have you checked what the compiler actually produces? If the data structures are well aligned usually the current compiler do know lots of tricks.Hidingyour intention behind some optimisation tricks may led that the compiler don't understand your intention. If the compiler is reasonable well they might use some other arithmetic tricks that are faster than single assembler commands (how many cycles take the div on your target)? Probably y = a/b; x = a%b; will produce what you want.If your target processor has a cache there are things you can do to increase the cache hit rate: It might be useful to check if any nested loops are 'the wrong way around' (swapping inner loop with outer loop) or thinking about the data structures (array of struct vs. struct of array).

author: aurmer
author_rank: Regular Contributor
timestamp: December 16, 2016, 11:05:50 pm
content: here are my resultsI set an interrupt timer to go off after 0x8000 cycles, and when the interrupt fired, each of these code blocks (which perform the exact task) performed as follows.Code:i = 0;int j = 5;int quotient = 0;int remainder = 0;int this_op_represents_how_long_it_takes_to_access_quot = 0;int this_op_represents_how_long_it_takes_to_access_rem = 0;TMR1 = 0;while(1){i++;quotient = j / 2;remainder = j % 2;this_op_represents_how_long_it_takes_to_access_quot = quotient;this_op_represents_how_long_it_takes_to_access_rem = remainder;}0x024A (decimal 586) times performed56 cycles per loopCode:i = 0;int j = 5;div_t divresult;int this_op_represents_how_long_it_takes_to_access_quot = 0;int this_op_represents_how_long_it_takes_to_access_rem = 0;TMR1 = 0;while(1){i++;divresult = div(j,2);this_op_represents_how_long_it_takes_to_access_quot = divresult.quot;this_op_represents_how_long_it_takes_to_access_rem = divresult.rem;}0x02E9 (decimal 745) times performed44 cycles per loopCode:i = 0;this_op_represents_how_long_it_takes_to_access_quot = 0;this_op_represents_how_long_it_takes_to_access_rem = 0;TMR1 = 0;while(1){i++;asm volatile("MOV _j, w4");asm volatile("MOV #2, w5");asm volatile("REPEAT #0x11");asm volatile("DIV.SW w4, w5");asm volatile("MOV w0, _this_op_represents_how_long_it_takes_to_access_quot");asm volatile("MOV w1, _this_op_represents_how_long_it_takes_to_access_rem");}0x0493 (decimal 1171) times performed28 cycles per loopThis is a demonstration of how the inline assembly can do it. I am just asking if C has a "perfectly" streamlined/optimized operator for this.

author: bson
author_rank: Unknown
timestamp: December 16, 2016, 11:19:44 pm
content: If I remember correctly the free XC8 compiler doesn't even permit use of hardware MUL... and it's not like you can just inline the asm or machine code for it either, because the assembler won't accept it. Presumably so you can't circumvent use of their library. This and some other completely idiotic features is why I switched away from PIC to MSP430 and use of either TI's compiler ($10 with the purchase of a LaunchPad) or gcc. Shitty tools is really the worst place to begin any project.

author: Ian.M
author_rank: Super Contributor
timestamp: December 17, 2016, 01:00:03 am
content: Quote from: bson on December 16, 2016, 11:19:44 pmEither your memory is incorrect or the hardware multiplier has been enabled for free mode in a later XC8 version to the one you were using.Code:l649:;main.c: 18: unsigned int t;;main.c: 19: t=PORTA*PORTB;movf((c:3968)),c,w;volatilemulwf((c:3969)),c;volatilemovffprodl,(c:main@t)movffprodh,(c:main@t+1)line20That's a snippet of the .as file from a PIC18 test program that contained:Code:void main(void){unsigned int t;t=PORTA*PORTB;built in FREE mode using XC8 v1.35.

author: JPortici
author_rank: Super Contributor
timestamp: December 17, 2016, 11:34:49 am
content: Optimizations used?I used to think i was smarter than the compiler but XC16 is actually good, so i let go. of course assembly is best for raw performance, but this seems like a benchmark: useless in the real world, where the sanity you keep from writing C is work a couple o megahertz more to keep up with performanceWith no optimization the code is a bit bulky and reloads variables and relinks at every unnecessary occasion, but enabling standard optimization should result in your assembly code, give or take one instruction.

author: bktemp
author_rank: Super Contributor
timestamp: December 17, 2016, 11:54:28 am
content: C30/XC16 have a lot of buildin functions for making use of the nice instruction set without the need of inline assembly:QuoteThere is also __builtin_divmodud for unsigned division.

author: John Coloccia
author_rank: Super Contributor
timestamp: December 17, 2016, 12:06:12 pm
content: You must have optimizations turned off. If not, the compiler would simply remove all loop that never does anything and never exits, right? I'm assuming it would, anyhow.Why not turn on optimizations, and then use the real time clock (assuming there is one) to do a real timing analysis on the straight forward version of the code. Why are you trying to save a handful of cycles, anyhow? Are you starved for resources?

author: bson
author_rank: Unknown
timestamp: December 17, 2016, 10:17:34 pm
content: Quote from: Ian.M on December 17, 2016, 01:00:03 amThey must have come to their senses and enabled it in the free version.

author: Ian.M
author_rank: Super Contributor
timestamp: December 17, 2016, 11:56:07 pm
content: Quote from: bson on December 17, 2016, 10:17:34 pmWell I've just checked v1.10 (August 2012) and it generates the same MULWF instruction so you must have been using a *really* old version.

author: julian1
author_rank: Frequent Contributor
timestamp: December 18, 2016, 12:21:36 am
content: It's a function of the quality of the compiler. Gcc on x86 will do it. Eg. take test function to compute result and remainder and return the sum.Quotewithout optimisation gcc gives generates two division instructions (and lots of stack shuffling) then adds themQuotecompiled with -O2 optimisation uses a single idiv to get the result,Quote

author: aurmer
author_rank: Regular Contributor
timestamp: December 20, 2016, 01:58:55 am
content: Thanks all for the discussion. This was a great learning experience. The _builtin_divmodsd is what I will probably go with in the end.I didn't understand the -O compiler flag when I posed this question, but since then I have registered for a 60 trial of the PRO version just to see what the -O3 can do. You are right, at the -O3 level of optimization, it figures out how to just do the DIV once.

author: JPortici
author_rank: Super Contributor
timestamp: December 20, 2016, 06:29:06 am
content: o3? o1 should already do the trickor at least be good enough so you don't have to pay for the lic.... hey wait you can recompile the compiler using the sources provided by microchip and patch away the licensing bit. completely legal, the license part is more a lazy tax. user karel made a comprehensive guidehttps://www.eevblog.com/forum/microcontrollers/pic32-evolution/msg1007099/#msg1007099