This discussion has been locked.
You can no longer post new replies to this discussion. If you have a question you can start a new discussion

Design question about global variable

Hello

Normall in an application we define global variable as:
int a;
int b;
...

How about use one structure to include all variables, and just define one structure instance? as below:
struct{ int a; int b; ...
}global;

Is the performance slower? does it take more code space? Is there any other drawback?

Thanks.

/Wang

Parents Reply Children
  • More words in the first instance on your part would have been appropriate.

    Why not check out my blog at http://www.kneeltronicals.com ? There's an entry about the importance of being precise.

  • Operating on packed data is very expensive. Some nice processors have intelligent memory controllers so the only cost is extra stalls because the memory controller must perform multiple memory accesses to be able to merge bytes and supply the requested data.

    But when the memory controller do not have that feature, and unaligned access generates an exception. So the compiler need to make sure there are no unaligned access - i.e. the compiler must generate code that make separate reads and then combines the data from the reads. Now there aren't just multiple memory accesses for the data, but multiple instructions costing extra code space, extra code fetches, extra instruction cycles, ...

    There are really only two times when a program should consider operating on packed data:
    - when packing/unpacking data for some I/O to be compatible with a standard (like a file format) or to communicate normalized data to another entity (like communication protocols).
    - when having an array of bulk data - basically a "database" - where the storage space of the array is more important than the cost of accessing the data.
    Often, it's better to handle the above two alternatives with specific code instead of any "packed" keyword.

    p = send_buffer;
    p += insert_32(p,a);
    p += insert_32(p,b);
    p += insert_8(p,c);
    p += insert_8(p,d);
    p += insert_16(p,e);
    p += insert_pad(p,32);
    p += insert_32(p,checksum(send_buffer,p-send_buffer));
    size = p - send_buffer;
    res = write_message(p,size);
    


    The above code makes it clearly visible what happens without any head scratching or need to look at any assembler output to make sure the compiler really did pack the data.

    Having all global variables packed is none of the above. It is just a terrible idea to consider. Another thing. If compiler/linker are smart enough to notice that the struct always starts properly aligned but with packed members, the compiler could decide that some specific members could be accessed directly while some other members need to be packed/unpacked. If the compiler would do such an optimization, then the addition/removal of a single member could change that, giving a big change in runtimes for all the different parts of the program. If the compiler/linker don't have any such logic, it must treat everything as unaligned, making sure that all parts of the program will suffer the extra code size and extra execution times.

  • But when the memory controller do not have that feature, and unaligned access generates an exception

    Yes, I saw this exception happens on iPhone's processor, I reuse a structure on iPhone and PC for socket communication, when cast a *void to *int pointer, sometimes I get exception.

    Some nice processors have intelligent memory controllers so the only cost is extra stalls because

    When unaligned, is the extra job done by compiler or CPU hardware??


    p = send_buffer;
    p += insert_32(p,a);
    p += insert_32(p,b);
    p += insert_8(p,c);

    I have lots of variable, from 20 to 50 maybe, some is char, some is int, this manual code easily make mistakes, specially when I add or remove some variables

    Now I have another idea to dump the variables for pc, no RAM is required, just define a ROM structure array, and a small function for data dump:

    1, Normally define variables, such as: int a, int b, int c

    2, Define a "smart" structure for dump only

    // Global variables
    int a;
    int b;
    int c;
    char d;
    short e;
    
    // A smart structure to describe each global variable
    typedef struct{
            void *var_ptr;
            uint8_t n_bytes;
    }var_t;
    
    // A descriptor for all global variables
    const var_t vars[]={
      {&a, sizeof(a)},
      {&b, sizeof(b)},
      {&c, sizeof(c)},
      {&d, sizeof(d)},
      {&e, sizeof(e)}
    };
    
    // Dump to a buffer, or send to UART...
    void dump(uint8_t *buf)
    {
            int i=0, j=0;
            const var_t *v=0;
            const uint8_t *p=0;
    
            for(i=0;i<sizeof(vars)/sizeof(var_t);i++){
                    v=&(vars[i]);
                    p=(uint8_t *)v->var_ptr;
                    for(j=0;j<v->n_bytes;j++){
                            *(buf++)=*(p++);
                    }
            }
    }
    

    Is this good to use?

  • "When unaligned, is the extra job done by compiler or CPU hardware??"

    When working with an architecture where the memory controller can perform unaligned accesses, then the compiler don't need to care. It generates code for one memory access, and the memory controller masks the unalign issue - all it costs is extra clock cycles. How many extra clock cycles depends on if the data is cached or if there is a need for extra fetches from RAM.

    "[...] this manual code easily make mistakes, specially when I add or remove some variables"
    Yes - but when adding/removing variables that you want to communicate through a file or to another program running on another side of a communication link, you always need to care about the details. Either by using a tagged protocol like XML, where the receiver can see which fields are sent. Or you _must_ make sure that both sides knows exactly what data you have packed.

    Letting the compiler pack the data will fail just as badly if you the other side doesn't have the exact same number of member fields in the same order and of the same size.

    But if you do need to write to a file or perform communication, then you shouldn't just make some code changes. You should have a document that descries exactly what data is sent and exactly how it is packaged. And - because of versioning issues - you should consider sending some version information so that you can catch situations when one side is newer than the other.

    "packed" is just magic that hides the complexity. And makes the developer forget that it is there. Until the very big crash/burn when the developer did something stupid, like changing the order of two members of the struct. Everything looked ok, except that the receiver unpacked something completely different.

    With explicit code for pack/unpack, a developer will know that he/she must care. And at least on the unpack side, the compiler can catch when a get_xx(p,var) is called with an incompatible variable type.

    Information hiding often sounds great. But can hurt a lot. One of the biggest problems with C++ is that code can look simple. But the reader don't get that the operators used are overloaded. And that there are magic constructors/destructors running totally invisible.

    The reason why we try to use all-uppercase for #define SYMBOL is to make sure a reader knows that there are magic "search/replace" going on and that magic could potentially be hidden, giving a totally different outcome than what the source code seems to imply.

    So "packed" may be convenient. But it's a great way to get hurt. It doesn't show that the original struct was matched against a specific file format specification of a JPEG image file. So it isn't obvious that adding a field or changing a type will suddenly break the processing of JPEG files.

    About your dump code - it shouldn't just focus on variable sizes. There is a huge difference between a 4-byte integer and an array of 4 characters. Consider byte order differences between little-endian and big-endian processors...
    Just remember that your array is basically the same as my suggested pack code - but with the important difference that my pack code was intended to pack an int32 into a generic format that isn't depending on what byte order the specific processor uses when storing the number in memory.

  • Interesting views. Person #1 states that packing reduces performance and then Person #2 suggests the use of XML.

    Why not check out my blog at http://www.kneeltronicals.com ? There's an entry about the importance of being objective.

  • Yes, but did you pick up what the view was?

    XML is a solution where all data is sent together with metadata, so that a listener can pick out exactly what data that was sent - and what wasn't sent.

    A protocol that just packs n variables binary after each other have no such metadata. Much more efficient use of the link. But with significant issues if fiels have changed offsets inside the binary package.

    So everything is a trade-off based on what the major goals was.

    When talking transfers, XML is more robust but with less efficiency than packed binary data.
    When talking about using global variables, then packed structs may be more space efficient but at the cust of code size and runtime.

    I wouldn't recommend a solution that hurt code size and access speed when running the program, just to make it a bit simpler to get a magic block of data to send to the other side. Especially when there are still lots of issues to get the other side to be able to make use of that binary blob.

  • Yes I did. Just pointing out the humour.

    Why not check out my blog at http://www.kneeltronicals.com ? There's an entry about the humour in opposite views that are both correct.

  • Why not check out my blog at http://www.kneeltronicals.com ? There's an entry about the humour in opposite views that are both correct.

    why would I ?

    wade through a ton of irrelevat stuff to find what is relevant to this issue

    BTW

    for Cortex M3 (and some others) DO NOT PACK!

    Erik

  • Erik Malund. You are as stubborn as your reputation!

    Why not check out my blog at http://www.kneeltronicals.com ? There's an entry about mules and their traits.

  • Why not check out my blog at http://www.kneeltronicals.com ? There's an entry about mules and their traits.
    because I love Google
    Google will get me to the informatin I am looking for and I do not have to wade through tons of blabber.
    nothing personal, I do not read blogs, I do not have facebook, I do not have twitter.

    if you want to dissiminate information put it where it goes do no make references to az place to search for it. If Instead of "Why not check out my blog" you could put up a link to the EXACT place where the relevant info was I would gladly look at it, but asking me to wade through a bunch of useless information will not work.

    Erik

  • No fear of that: http://www.kneeltronicals.com/ is purely fictitious - there is no content whatsoever.