Efficiency of structures of length 8 and uint64_t

TL; DR: Are 8-byte structures handled as efficiently as 8-byte uint64_t?

I have 3 data structures that are very similar. They are 6, 7 and 8 bytes long. I want to put them all in uint64_t variables. The goal is that comparisons and exercises will be very effective. (These values ​​are used as a key for several (large) trees).

Example. I have defined the following data structure for which the length is 7 bytes.

typedef struct {
  union {
    uint64_t raw;
    struct {
      uint8_t unused;
      uint8_t node_number;
      uint8_t systemid[SYSTEMID_LENGTH]; /* SYSTEMID_LENGTH is 6 bytes. */
    } nodeid;
  };
} nodeid_t;

Now I can perform quick assignments and copies through a raw union member.

nodeid_t x, y;

x.raw = y.raw
if (x.raw > y.raw) {

Etc etc.

My question is about using functions and assignments. When I pass this structure by value, whether the compiler (gcc) will recognize that these structures are 8 bytes long. And so treat as if they are int64_t?

: / :

int64_t my_function();
nodeid_t my_function();

, gcc 1 nodeid_t , ? 8 ? -O?

.

int64_t a, b;
nodeid_t x, y;

a = b; /* One machine instruction, I hope. */
x = y; /* Also one instruction, or will it do a loop ? */
+4
6

, union , uint64_t.

​​nodeid struct: struct. , .

uint8_t: .

, nodeid_t .

- nodeid_t: _t POSIX C.

+1

, , . . , :
1. , , , , char 1.
2. struct nodeid. gcc __attribute__((packed)). , MSVC #pragma push pack(1)...#pragma pop.
3. Gcc ( , ...), , STATIC_ASSERT(sizeof(nodeid_t) == sizeof(uint64_t))
4. 8 , , - . .. .

+1

. , , x86_64 ( ), ( ).

struct foo {
    char a;
    char b;
    short c;
    int d;
};

void
foo_copy(struct foo *a, struct foo *b)
{
    *a = *b;
}


extern void bar(struct foo a);
void
foo_value(void)
{
    struct foo f = { .a = 1 };
    bar(f);
}
$ cc -fomit-frame-pointer -O2 -S foo.c
$ cat foo.s
[... cleaned up ...]
_foo_copy:                              ## @foo_copy
    movq    (%rsi), %rax
    movq    %rax, (%rdi)
    retq

_foo_value:                             ## @foo_value
    movl    $1, %edi
    jmp _bar                    ## TAILCALL

, , , , ABI . ABI . . x86_64, , , , , , .

+1

- , , .

, , , , :

if (x.raw > y.raw) {

little-endian, . , , , , :

x.nodeid.systemid[0] = 1;
x.nodeid.systemid[1] = 2;
y.nodeid.systemid[0] = 2;
y.nodeid.systemid[1] = 1;

(x.raw > y.raw) true.

0

3 typedef uint64_t.

typedef uint64_t isis_simple_item_t;
typedef struct isis_complex_item_t {
  byte_t unused;
  byte_t node_number;
  byte_t systemid[ISIS_SYSTEMID_SIZE];
};

byte_t number;
isis_simple_item_t nodeid;

number = ((isis_complex_item) nodeid).node_number;

, , , , ..

, , , -. , uint64_t . , .

0

Instead of using all these fancy legs, why not just use it memcmp, it works for any continuous data type, probably implemented as an internal part of the compilation, therefore it should be fast and definitely bypassing the strict anti-aliasing rule.

0
source

All Articles