Jump to content
OMRON Forums

Computational efficiency


steveneads

Recommended Posts

Do you have any notes on writing applications to be as efficient as possible? I am in the process of deciding how to convert my existing UMAC code to PowerPMAC. I took a great deal of care in the UMAC code to make it run as fast as possible, because we have a lot of time critical background calculations. Although PowerPMAC is clearly faster, I want to make sure that I make the right design choices early on.

 

I've tried a few read/write experiments for getting data into and out of C code and this has already revealed some big timing differences based on the type of data. I've made a block of successive reads and writes of different types of variable and timed each block of 50 reads and 50 writes to get the following results (in microseconds):

1/ using GetPtrVar/SetPtrVar for shared memory doubles = 11.9

2/ using GetPtrVar/SetPtrVar for shared memory floats = 13.4

3/ using GetPtrVar/SetPtrVar for user shared memory floats = 16 to 20 (the time seems to vary)

4/ using pshm->P[xx] = 1.5 to 6 (the time seems to vary)

5/ using the name of the global variable directly = 1.4 to 5 (the time seems to vary)

6/ using pointer array declarations for user shared memory = 1.7 to 6 (the time seems to vary)

 

These results suggest that the GetPtrVar and SetPtrVar functions should be avoided if at all possible - particularly for user shared memory, and that using P variables is best, with pointer arrays also being quite good.

 

Do these findings match what you'd expect? Any words of wisdom would be appreciated!

Link to comment
Share on other sites

  • Replies 7
  • Created
  • Last Reply

Top Posters In This Topic

Here are some suggestions:

 

Avoid division as much as possible (e.g. precompute the inverse of a value and multiply it against things later, rather than dividing by this value every time)

 

Prioritize shifting (using << or >>) over multiplication when using integers

 

Use the smallest data types possible for your given variable (e.g. consider using a "char" instead of an int for boolean-style flags)

 

Precomputing constants and not repeating calculations

 

Use "static" data types to prevent reallocating memory every iteration

 

Using compound operators (e.g. -=) and prefix/postfix operators (e.g. -- and ++)

 

Try to linearize code as much as possible (e.g. instead of recursive algorithms, try a for-loop approach)

 

Implement timeouts and sleeps (nanosleep() is the best) to prevent while loops from consuming unnecessary CPU cycles or locking up the CPU

 

However, the majority of the slowdown comes from GetPtrVar and SetPtrVar. These are rather slow functions and we do not recommend using them for foreground tasks such as the RTICPLC. Instead, I recommend directly mapping the memory registers in question by means of C pointers. Do you know how to do this? If not, please take a look at pages 681 and following of the Power PMAC User Manual. By directly mapping I/O memory with C pointers, you can eliminate the function calls to Get/SetPtrVar.

Link to comment
Share on other sites

Hi Charles

 

Thanks for your comments - I was aware of avoiding divides as I've always done this in UMAC, but the more PowerPMAC related items are useful additional information for me.

 

Since you mention constants, can I use #define to set a value for a constant to be used throughout my C code and script code (#define g 9.81 for instance)? If so, where should this be done?

 

Regarding using pointers, I think that I understand this to some extent - but not fully! Here's an example that I've tried for reading values from our 16 bit analogue card (Acc36 UK) - which is made for us by DeltaTau UK (John White). I've set up a pointer variable array to look at these values:

 

ptr ADC(32)->*;

ADC(0)->i.io:$B18694.8.16;

ADC(1)->i.io:$B18698.8.16; etc...

 

In my C code I set up...

int *ADCval;

ADCval = (int*) piom + 2908581; //assign address location (1st address of Acc36UK card /4)

 

debug1 = ADCval[0]; //debug1 is a global

debug2 = ADCval[1];

 

I'm looking at debug1 and debug2 in the watch window, but the value is wrong because I'm seeing all 32 bits instead of the 16 true bits. What's the best way to deal with this?

 

Thanks

Steve

Link to comment
Share on other sites

Hi,

 

Yes, you can use #define statements to define constants. You can place this in the same ".c" file in which you're working if you want, or create a header to contain these definitions and then include the header with #include. Keep in mind that if you're using background C programs, you can keep common #defines all in headers you create in the Libraries folder. For realtime routines and BGCPLCs, however, you need to create the headers inside the same folder as the ".c" file with which you're working.

 

Regarding your C pointer, if you say you get the right number except shifted up 16 bits, try shifting down 16 bits, like this:

 

debug1 = ADCval[0] >> 16;

 

If it's actually at the bottom 16 bits and you want to get rid of the top 16, you can try

 

debug1 = (ADCval[0] << 16) >> 16;

 

This only works for unsigned integers, though. If it's a signed value, you'll need to multiply by 65536 to shift up by 16 bits or divide by 65536 to shift down 16, for example.

 

Masking and shifting is sometimes required, since in C, you can only make a pointer to a whole word, not to individual bits of the word.

 

Also note that you can technically map a pointer to an entire card and use structure dereferencing to access its members. See pages 682-683 for examples of what I mean. Please let me know if you need anything else.

Link to comment
Share on other sites

Hi Charles

 

With regard to defining constants, I was really wondering if there was a simple way to define them for both the C code and the Script so that it could be done in one place only?

 

Before getting your response on the bit shifting point, I had already changed my code to read:

 

debug1 = (short int) (ADCval[0] >> 8) ;

 

This seems to work correctly, does this seem a sensible way (the best way?) to extract the 16 bit signed value from the middle of the 32 bit word?

Link to comment
Share on other sites

Yes, you can create a file that can be included in both Script and C.

 

First, create a .pmh file in your Global Includes folder under Script Language (e.g. "asharedwithcapp.pmh").

 

Then, in your C program, you can include it like this:

 

#include "../../../PMAC Script Language/Global Includes/asharedwithcapp.pmh"

 

Yes, shifting is fine to get the desired value out of the register. I am not super familiar with the ACC-36E UK product, but I assume that when using it with Power PMAC, the lowest 8 bits in the ADC register are useless and the useful data is only in bits [31:8]. So, to get the ADC at the lower 12 bits, you can first shift up 12 bits, then down 20. For the higher 12 bits, just shift down 20.

Link to comment
Share on other sites

Thanks for your assistance.

 

FYI, the A/D card is 16 bits starting from bit 8 - hence the casting to "short int" in my code. This does seem to work correctly for both positive and negative numbers. Does the shifting up do something that the cast doesn't? I don't really see why you need it in this case!

Link to comment
Share on other sites

Guest
This topic is now closed to further replies.

×
×
  • Create New...