dpdk存储系统

NUMA系统简介

普通的南北桥系统里面主要是通过北桥进行数据的交换,这样系统的瓶颈在北桥中,NUMA(非一致性内存架构Non-Uniform Memory Architecture)系统可以解决这样的系统瓶颈。在这种架构下,在一个配有四核的机器中,不需要一个复杂的北桥就能将内存带宽增加到之前的四倍,这样有一个问题就是该系统中,访问内存花费的时间和处理器相关,之所以和处理器相关是因为该系统每个处理器都有本地内存,访问本地内存时间很短,但是访问远端内存时间较长。

软件预取

软件预取指令

预取指令可以把即将用到的数据从内存中加载到cache中,这样当前数据处理完毕后,即将用到的数据已经在cache中,大大减少了从内存中读取的开销。也减少了内存读取的时间。

通过软件预取指令,可以显式的加载数据到cache,以提高程序执行的效率。

预取指令列表:

指令 解释
PREFETCH0 将数据存放在每一级cache,假设有三级cache,则L1、L2、L3 cache都包含该数据的一个备份
PREFETCH1 将数据存放在除了L1 cache之外的每一级cache,假设有三级cache,则L2、L3 cache都包含该数据的一个备份
PREFETCH2 将数据存放在除了L1、L2 cache之外的每一级cache,假设有三级cache,则L3 cache包含该数据的一个备份
PREFETCHNTA 和PREFETCH0功能类似,区别是数据是作为非临时数据存放,在使用完一次之后,cache认为该数据是可以被淘汰出去的

预取指令是汇编指令,C程序库中的mmintrin.h包含了如下的程序模型:

void _mm_prefeatch(char *p, int i);

p是需要预取的内存地址,i对应相应的预取指令。

DPDK中的预取

DPDK处理报文的逻辑是:

  1. 写接收描述符到内存,填充数据缓冲区指针,网卡收到报文后就会根据这个地址把报文内容填充进去。
  2. 从内存中读取接收描述符(当收到报文时,网卡会更新该结构)(内存读),从而确认是否收到报文。
  3. 从接收描述符确认收到报文时,从内存中读取控制结构体的指针(内存读),再从内存中读取控制结构体(内存读),把从接收描述符读取的信息填充到该控制结构体。
  4. 更新接收队列寄存器,表示软件收到了新的报文。
  5. 内存中读取报文头部(内存读),决定转发端口。
  6. 从控制结构体把报文信息填入到发送队列发送描述符,更新发送队列到寄存器。
  7. 从内存中读取发送描述符(内存读),检查是否有包被硬件传送出去。
  8. 如果有的话,从内存中读取相应控制结构体(内存读),释放数据缓冲区。

DPDK处理一次报文要读取6次报文,所以必须要保证cache命中,否则性能下降会非常严重。

以下代码截取于/examples/ip_reassembly/main.c

while (1) {

        cur_tsc = rte_rdtsc();

        diff_tsc = cur_tsc - prev_tsc;
        if (unlikely(diff_tsc > drain_tsc)) {

            for (portid = 0; portid < RTE_MAX_ETHPORTS; portid++) {
                if ((enabled_port_mask & (1 << portid)) != 0)
                    send_burst(qconf, 1, portid);
            }

            prev_tsc = cur_tsc;
        }
        //从接收队列读数据包
        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);

            //预取数据包
            for (j = 0; j < PREFETCH_OFFSET && j < nb_rx; j++) {
                rte_prefetch0(rte_pktmbuf_mtod(
                        pkts_burst[j], void *));
            }

            //预取并转发已预取的数据包
            for (j = 0; j < (nb_rx - PREFETCH_OFFSET); j++) {
                rte_prefetch0(rte_pktmbuf_mtod(pkts_burst[
                    j + PREFETCH_OFFSET], void *));
                reassemble(pkts_burst[j], portid,
                    i, qconf, cur_tsc);
            }

            //转发剩余的预取包
            for (; j < nb_rx; j++) {
                reassemble(pkts_burst[j], portid,
                    i, qconf, cur_tsc);
            }

        }
    }

猜你喜欢

转载自blog.csdn.net/u012630961/article/details/80974822