IEncinas

C tricks

Random collection of C tricks that I found interesting.

Zero size struct members to enforce cache-line grouping of other struct elements

This patch, using the zero length extension, makes sure at compile-time that certain struct elements are located in the same cache line.

1#include <stdio.h>
2
3struct k {
4  int a[0];
5};
6
7int main() { printf("%zu\n", sizeof(struct k)); }

Should print print a 0. I was aware of the other uses of zero-length arrays for dynamically-sized structs (where those zero-length arrays are put at the last position of a struct, see the extension for more information about this), but hadn’t seen this trick before.

Using macros, zero-sized arrays are defined on group boundaries:

1#ifndef __cacheline_group_begin
2#define __cacheline_group_begin(GROUP) \
3	__u8 __cacheline_group_begin__##GROUP[0]
4#endif
5
6#ifndef __cacheline_group_end
7#define __cacheline_group_end(GROUP) \
8	__u8 __cacheline_group_end__##GROUP[0]
9#endif
 1/* TX readonly hotpath cache lines */
 2__cacheline_group_begin(netns_ipv4_read_tx);
 3u8 sysctl_tcp_early_retrans;
 4u8 sysctl_tcp_tso_win_divisor;
 5u8 sysctl_tcp_tso_rtt_log;
 6u8 sysctl_tcp_autocorking;
 7int sysctl_tcp_min_snd_mss;
 8unsigned int sysctl_tcp_notsent_lowat;
 9int sysctl_tcp_limit_output_bytes;
10int sysctl_tcp_min_rtt_wlen;
11int sysctl_tcp_wmem[3];
12u8 sysctl_ip_fwd_use_pmtu;
13__cacheline_group_end(netns_ipv4_read_tx);

The macros basically expand to unique group identifiers within a struct (otherwise, a compilation error would happen). These groups can later be used to check if a variable is within a group:

1#ifndef CACHELINE_ASSERT_GROUP_MEMBER
2#define CACHELINE_ASSERT_GROUP_MEMBER(TYPE, GROUP, MEMBER) \
3	BUILD_BUG_ON(!(offsetof(TYPE, MEMBER) >= \
4		       offsetofend(TYPE, __cacheline_group_begin__##GROUP) && \
5		       offsetofend(TYPE, MEMBER) <= \
6		       offsetof(TYPE, __cacheline_group_end__##GROUP)))
7#endif

Or to check a group size:

1#ifndef CACHELINE_ASSERT_GROUP_SIZE
2#define CACHELINE_ASSERT_GROUP_SIZE(TYPE, GROUP, SIZE) \
3	BUILD_BUG_ON(offsetof(TYPE, __cacheline_group_end__##GROUP) - \
4		     offsetofend(TYPE, __cacheline_group_begin__##GROUP) > \
5		     SIZE)
6#endif

See the patch for more information. The relevance of the patch has nothing to do with this small trick, but I found it to be quite interesting :)