这段代码中union的意义是什么,structure的缺点是什么?
What is the significance of union in this code, what is the disadvantage if 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)
上面代码中,typedef union queue_entry_u,union有什么意义。如果我们取structure(typedef struct queue_entry_u),有什么缺点吗?
unions
有几种用法:
union
节省一些内存。它使得 s
和 pad
位于内存中的同一个位置。如果您知道只需要其中一个,那么您可以使用 union
,这很有用。
- 能够遍历结构中的字段也很有用。通过将字段保存在联合中,您同时拥有一个数组和一个结构,因此如果您遍历
pad
,您实际上是在遍历 s
的字节。
unions
通常也可用于铸造。仅使用 union
将您的条目序列化为字节数组的语法更漂亮一些。
- 在这种情况下,使用
union
似乎是为了填充 s
的大小以适应高速缓存行。这样,如果 queue_entry_s
的大小恰好是缓存行 s
长度的倍数,那么 pad
将位于完全相同的内存中,而不会浪费 space。否则 pad
将比 s
占用更多内存,并且 union
的大小将始终是缓存行长度的精确倍数。
也就是说,如果您正在为内存非常低或性能要求非常严格的设备编写嵌入式代码,那么使用 unions
通常是个好主意。它们非常危险并且很容易被误用,因为意外地覆盖了本应代表 union
中的另一种类型的内存。
让我们从 K&R 第 2 版中联合的定义开始:
A union is a variable that may hold (at different times) objects of
different types [...]. Unions provide a way to manipulate different
kinds of data in a single area of storage.
问题中的联合包含两个对象:struct queue_entry_s
类型的结构和uint8_t
类型的数组。重要的是要注意这两个对象在内存中重叠。具体来说,结构开始的地址与数组开始的地址相同。如果写入结构体,数组的内容就会改变,如果写入数组,那么结构体的内容就会改变。
然后注意 ODP_CACHE_LINE_SIZE_ROUNDUP
宏取一个大小并计算大于或等于该大小的 64 的最小倍数。
联合体的大小由最大成员的大小决定。因此,例如,如果 sizeof(struct queue_entry_s)
为 80,则 pad
数组的 sizeof 将为 128,并且 union 的 sizeof 将为 128。
这终于给我们带来了答案。 union的目的是增加结构体使用的内存,使结构体总是使用64字节内存的倍数。
如果您将 typedef union queue_entry_u
更改为 typedef struct queue_entry_u
,那么内存布局将发生变化。 s
和 pad
在内存中重叠,pad
数组将遵循内存中的 s
结构。所以如果 s
占用 80 个字节, pad
占用 128 个字节,那么 typedef struct queue_entry_u
将定义一个占用 208 个字节内存的对象。那会浪费内存,并且不符合 64 的倍数要求。
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)
上面代码中,typedef union queue_entry_u,union有什么意义。如果我们取structure(typedef struct queue_entry_u),有什么缺点吗?
unions
有几种用法:
union
节省一些内存。它使得s
和pad
位于内存中的同一个位置。如果您知道只需要其中一个,那么您可以使用union
,这很有用。- 能够遍历结构中的字段也很有用。通过将字段保存在联合中,您同时拥有一个数组和一个结构,因此如果您遍历
pad
,您实际上是在遍历s
的字节。 unions
通常也可用于铸造。仅使用union
将您的条目序列化为字节数组的语法更漂亮一些。- 在这种情况下,使用
union
似乎是为了填充s
的大小以适应高速缓存行。这样,如果queue_entry_s
的大小恰好是缓存行s
长度的倍数,那么pad
将位于完全相同的内存中,而不会浪费 space。否则pad
将比s
占用更多内存,并且union
的大小将始终是缓存行长度的精确倍数。
也就是说,如果您正在为内存非常低或性能要求非常严格的设备编写嵌入式代码,那么使用 unions
通常是个好主意。它们非常危险并且很容易被误用,因为意外地覆盖了本应代表 union
中的另一种类型的内存。
让我们从 K&R 第 2 版中联合的定义开始:
A union is a variable that may hold (at different times) objects of different types [...]. Unions provide a way to manipulate different kinds of data in a single area of storage.
问题中的联合包含两个对象:struct queue_entry_s
类型的结构和uint8_t
类型的数组。重要的是要注意这两个对象在内存中重叠。具体来说,结构开始的地址与数组开始的地址相同。如果写入结构体,数组的内容就会改变,如果写入数组,那么结构体的内容就会改变。
然后注意 ODP_CACHE_LINE_SIZE_ROUNDUP
宏取一个大小并计算大于或等于该大小的 64 的最小倍数。
联合体的大小由最大成员的大小决定。因此,例如,如果 sizeof(struct queue_entry_s)
为 80,则 pad
数组的 sizeof 将为 128,并且 union 的 sizeof 将为 128。
这终于给我们带来了答案。 union的目的是增加结构体使用的内存,使结构体总是使用64字节内存的倍数。
如果您将 typedef union queue_entry_u
更改为 typedef struct queue_entry_u
,那么内存布局将发生变化。 s
和 pad
在内存中重叠,pad
数组将遵循内存中的 s
结构。所以如果 s
占用 80 个字节, pad
占用 128 个字节,那么 typedef struct queue_entry_u
将定义一个占用 208 个字节内存的对象。那会浪费内存,并且不符合 64 的倍数要求。