Cache Miss的代价非常高,回内存读需要65纳秒,可以将即将访问的数据主动推送的CPU Cache进行优化。比较典型的场景是链表的遍历,链表的下一节点都是随机内存地址,所以CPU肯定是无法自动预加载的。但是我们在处理本节点时,可以通过CPU指令将下一个节点推送到Cache里。
操作码 指令 Description
0F 18 /1 PREFETCHT0 m8 预取数据到所有级别的缓存,包括 L0 。
0F 18 /2 PREFETCHT1 m8 预取数据到除 L0 外所有级别的缓存。
0F 18 /3 PREFETCHT2 m8 预取数据到除 L0 和 L1 外所有级别的缓存。
0F 18 /0 PREFETCHNTA m8 预取数据到非临时缓冲结构中,可以最小化对缓存的污染。
static inline void rte_prefetch0(volatile void *p)
{
asm volatile ("prefetcht0 %[p]" : [p] "+m" (*(volatile char *)p));
}
/*
* Read packet from RX queues
*/
for (i = 0; i < qconf->n_rx_queue; i++) {
portid = qconf->rx_queue_list[i].portid;
nb_rx = rte_eth_rx_burst(portid, 0, pkts_burst,
MAX_PKT_BURST);
/* Prefetch first packets */
/* Configure how many packets ahead to prefetch, when reading packets
#define PREFETCH_OFFSET 3*/
for (j = 0; j < PREFETCH_OFFSET && j < nb_rx; j++) {
rte_prefetch0(rte_pktmbuf_mtod(
pkts_burst[j], void *));
}
/* Prefetch and forward already prefetched packets */
for (j = 0; j < (nb_rx - PREFETCH_OFFSET); j++) {
rte_prefetch0(rte_pktmbuf_mtod(pkts_burst[
j + PREFETCH_OFFSET], void *));
l3fwd_simple_forward(pkts_burst[j], qconf, i, portid);
}
}
CPU读取数据的效率为:
L1:3-5个时钟周期
L2:十几个时钟周期
L3:几十个时钟周期
内存:几百个时钟周期
cache的基本操作单位是cache line,如果一个cache line中包含两个不同的数据,那么如果一个线程对其中一个数据进行写操作,那么这个cache line就需要更新,意味着另一个线程需要重新加载另一个数据。所以两个不同的数据不要放在同一个cache line中,这就需要数据定义的时候声明cache对齐,例如cache line的大小是128位(16字节),定义一个8字节的long,剩下8字节使用无用的数据来填充。
DPDK中声明cache line对齐的关键字:__rte_cache_aligned