What is the point of combining in this code, what is the flaw in the structure?

struct queue_entry_s {

    odp_buffer_hdr_t *head;
    odp_buffer_hdr_t *tail;
    int               status;

    enq_func_t       enqueue ODP_ALIGNED_CACHE;
    deq_func_t       dequeue;
    enq_multi_func_t enqueue_multi;
    deq_multi_func_t dequeue_multi;

    odp_queue_t       handle;
    odp_buffer_t      sched_buf;
    odp_queue_type_t  type;
    odp_queue_param_t param;
    odp_pktio_t       pktin;
    odp_pktio_t       pktout;
    char              name[ODP_QUEUE_NAME_LEN];
};

typedef union queue_entry_u {
    struct queue_entry_s s;
    uint8_t pad[ODP_CACHE_LINE_SIZE_ROUNDUP(sizeof(struct queue_entry_s))];
} queue_entry_t;

typedef struct queue_table_t {
    queue_entry_t  queue[ODP_CONFIG_QUEUES];                                                                                           
} queue_table_t;

static queue_table_t *queue_tbl;

#define ODP_CACHE_LINE_SIZE 64

#define ODP_ALIGN_ROUNDUP(x, align)\                                                                                                   
    ((align) * (((x) + align - 1) / (align)))

#define ODP_CACHE_LINE_SIZE_ROUNDUP(x)\
    ODP_ALIGN_ROUNDUP(x, ODP_CACHE_LINE_SIZE)

      

In the above code typedef union queue_entry_u . What is the meaning of unification. If we take a structure ( typedef struct queue_entry_u ), is there any downside?

+3


source to share


2 answers


unions

have several use cases:

  • union

    saves some memory. It does so s

    , and pad

    sat in the same place in memory. Helpful if you know you only need one of them you can use union

    .
  • It's also helpful to be able to iterate over the fields in your structure. By keeping the fields in concatenation, you have both an array and a structure, so if you iterate over pad

    , you are essentially iterating over bytes s

    .
  • unions

    also useful in general for casting. The syntax is a little prettier to serialize your entry to a byte array just using union

    .
  • In this case, the use union

    is to insert the size s

    according to the cache line. Thus, if the size of a queue_entry_s

    is an exact multiple of the length of the cache line s

    , then it pad

    will be in exactly the same memory, not in space. Otherwise it pad

    will consume more memory than s

    , and the size union

    will always be a multiple of the cache line length.


That being said, it is generally recommended to use unions

if you are writing inline code for devices with very low memory or very stringent performance requirements. They are very dangerous and very easy to misuse by accidentally writing memory intended to represent a different type in union

.

+8


source


Let's start by defining a union from the K&R 2nd edition:

Union is a variable that can contain (at different times) objects of different types [...]. Unions provide a way to manipulate different kinds of data in a single storage area.

The union in the question contains two objects: a type structure struct queue_entry_s

and an array uint8_t

. It is important to note that these two objects overlap in memory. In particular, the address where the structure starts is the same as the address where the array begins. If you write to a structure, the contents of the array will change, and if you write to an array, then the contents of the structure will change.

Then notice that the macro ODP_CACHE_LINE_SIZE_ROUNDUP

takes a size and calculates the smallest multiple of 64 that is greater than or equal to that size.



The size of the union is determined by the size of the largest member. So, for example, if sizeof(struct queue_entry_s)

equal to 80, then the sizeof of the array pad

will be 128 and the size of the union will be 128.

Which brings us finally to the answer. The purpose of the union is to increase the memory used by the structure so that the structure always uses a multiple of 64 bytes of memory.

If you were to change typedef union queue_entry_u

to typedef struct queue_entry_u

, then the memory layout will be changed. Instead of having s

and pad

overlapping in memory, the array pad

will follow the structure s

in memory. Therefore, if it s

occupies 80 bytes and pad

occupies 128 bytes, then it typedef struct queue_entry_u

will define an object that occupies 208 bytes of memory. This will be a waste of memory and will not meet the multi-64 requirements.

+1


source







All Articles