We are running a survey to help us improve the experience for all of our members. If you see the survey appear, please take the time to tell us about your experience if you can.
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
Is this a trick to get fewer "global variables"?
The access to that big struct is quite efficient as long as a register is pointing at the struct and the code then specifies the offset of each field.
So specially for Thumb-mode code, this can work very well. But note that Thumb and Thumb2 have different limits for how large offsets they can span.
Write the code so _you_ feel it is easy to maintain. Group globals in structs or close together if they relate to each other. Preferably see if they can get names that help to tell they inter-relate.
But don't use "number of globals" as your main design criteria.
Thanks for your replay, I have 2 reasons to use the big structure, but not sure should I use it in a big project.
1. when in debugging, in watch window, I just type "global", then can see all global variables in that structure, else, I need to type all the variable name.
2. I want to dump any of the global variables to PC through UART/USB, and unpack them on PC, if I reuse the same structure on pc and keep the same memory layout/alignment, I can unpack it easily!
I just want to know is there any drawback, or is any other better ways?
That argument doesn't work. Once you've put all your globals in there, you're going to have to type "global" and then the variable name, just like you now have to type the name --- because that global structure will be every bit as big as your current list of global variables.
No, you can't. Because there's no guarantee whatsoever that the memory layout of that struct will be the same on both ends. Not even if you firmly believe it should be.
Yes, there is a drawback: you'll waste memory by forcing the compiler to place all those variables in a particular sequence, instead of allowing it to choose an optimal sequence.
And your incremental project compilation times will go up, because that global struct will tie all modules together so tightly that any change to one of them will effectively trigger a full rebuild of the entire program, even if only, say, 2 C files actually need that particular variable.
And yes, there is a better way: don't do that.
If you want to re-use the same struct on PC, best take care, that the variables are 4-byte aligned inside the struct.
If you work only with int's, this should be no problem.
But if you want to define also char's, then you should always define blocks of 4 successive char's - if you define also short's, then blocks of 2 successive short's (or 1 short and 2 successive char's) also should be ok. (So if you want to define 3 char's inside this struct, then add a 4th one and call it cFree or so ... ).
PS: Generally it is always a good idea to group variables in structs, if you have a larger program with several modules - just perhaps better use several structs - just 1 struct sounds a bit strange.
you should look at #pragma pack to pack your structures
Thank you to point out this possibility (but I would not recommend packing, unless really required - if you pack the structures and you have an uneven number of char's and short's in your structs, you will run into alignment problems - that's why I recommended to fill with "free" char's / short's).
NO NO NO !!!
this reduces performance, and, requires that every pointer that addressed that data is also packed. Still sounds like a good idea?
a pointer to the packed structure does not have to be packed! it must be declared as a pointer to a packed structure.
Why not check out my blog at http://www.kneeltronicals.com ?
Well, that is exactly what I meant.
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.
Yes, it does, for applications where extreme speed or code density is not very important. That would be a majority of applications, by the way.
@Mike Kleshov,
Well in my opinion, this is a very unwise approach. But this is merely my opinion.
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?
Withing the existing structure of your program the solution you are proposing could make a lot of sense. Whether that structure is good or bad is a different matter, of course. My guess is that it can be improved.