cache line bytes是指一次性从内存读到cache中的字节数。cache line对齐的目的,可减少CPU访问cache、cache访问内存的次数。因为数据跨越两个cache line,就意味着两次load或者两次store。如果数据结构是cache line对齐的, 就有可能减少一次读写。
/*
* Allow CFLAGS to override the configured / deduced cache line size
*/
#ifndef CLIB_LOG2_CACHE_LINE_BYTES
/* Default cache line size of 64 bytes. */
#ifndef CLIB_LOG2_CACHE_LINE_BYTES
#define CLIB_LOG2_CACHE_LINE_BYTES 6
#endif
#endif /* CLIB_LOG2_CACHE_LINE_BYTES defined */
#if (CLIB_LOG2_CACHE_LINE_BYTES >= 9)
#error Cache line size 512 bytes or greater
#endif
#define CLIB_CACHE_LINE_BYTES (1 << CLIB_LOG2_CACHE_LINE_BYTES)
#define CLIB_CACHE_LINE_ALIGN_MARK(mark) u8 mark[0] __attribute__((aligned(CLIB_CACHE_LINE_BYTES)))
cache一致性问题:多核对同一块内存读写,会引起冲突的问题。
类似于DPDK,VPP为了避免cache一致性问题,对某些数据结构,给每个核都定义一份。
例如snat session定义成per-thread的
typedef struct
{
/* Main lookup tables */
clib_bihash_8_8_t out2in;
clib_bihash_8_8_t in2out;
/* Endpoint dependent sessions lookup tables */
clib_bihash_16_8_t out2in_ed;
clib_bihash_16_8_t in2out_ed;
/* Find-a-user => src address lookup */
clib_bihash_8_8_t user_hash;
/* User pool */
snat_user_t *users;
/* Session pool */
snat_session_t *sessions;
/* Pool of doubly-linked list elements */
dlist_elt_t *list_pool;
/* NAT thread index */
u32 snat_thread_index;
} snat_main_per_thread_data_t;
1)I-cache相关的优化
(1)精简的code path,vpp的node架构简化了调用关系,减少冗余代码;
(2)分支预测
/* Hints to compiler about hot/cold code. */
#define PREDICT_FALSE(x) __builtin_expect((x),0)
#define PREDICT_TRUE(x) __builtin_expect((x),1)
2)D-cache相关的优化
减少D-cache miss的数量,增加有效的数据访问的数量。
(1)Vector all over ,所有结构都是数组、链表操作也修改成为动态数组;
(2):利用gcc内置函数 __builtin_prefetch实现的数据手工预取
#define _CLIB_PREFETCH(n,size,type) \
if ((size) > (n)*CLIB_CACHE_LINE_BYTES) \
__builtin_prefetch (_addr + (n)*CLIB_CACHE_LINE_BYTES, \
CLIB_PREFETCH_##type, \
/* locality */ 3);
#define CLIB_PREFETCH(addr,size,type) \
do { \
void * _addr = (addr); \
\
ASSERT ((size) <= 4*CLIB_CACHE_LINE_BYTES); \
_CLIB_PREFETCH (0, size, type); \
_CLIB_PREFETCH (1, size, type); \
_CLIB_PREFETCH (2, size, type); \
_CLIB_PREFETCH (3, size, type); \
} while (0)
VPP的mheap自己管理内存分配。我们知道频繁的调用malloc和free函数是导致性能降低的重要原因,不仅仅是函数调用本身非常耗时,而且会导致大量内存碎片。由于空间比较分散,也进一步增大了cache misses的概率。