DPDK的RTE_RING中发送和接收单个消息的性能低下

时间:2018-09-01 00:36:58

标签: dpdk

地狱堆栈溢出专家。

我想知道当使用1个突发(tx rx队列)测试往返延迟时,是否有很好的解释说明由rte_ring引起的高延迟。

我已经使用两个节点(客户端和服务器)测试了性能 并计算单次乒乓球往返的经过延迟。 延迟是在两个不同的测试用例上计算的 1.直接从rx tx队列发送和接收的经过的延迟。 2.从rte_ring发送和接收的经过的延迟。

这是我在1个脉冲串(tx rx)上测试DPDK时生成的RTE_RING延迟的结果

enter image description here

DPDK + RING是使用RING发送乒乓消息的经过时间

DPDK是直接将消息发送到tx rx队列所经过的延迟。

RING是从DPDK延迟中减去DPDK + RING的假定延迟。

具有1个突发的直接TX接收队列的延迟 当我发送单个消息(512、1024、4096字节)(每个请求1个突发)并从远程服务器接收1个响应突发时。 等待时间约为4〜8微秒。

RTE_RING延迟为1次 当我使用rte_ring从客户端和服务器发送和接收数据时,延迟像疯了似的增加了,从59微秒到100微秒。

RTE_RING延迟10次 例如,当我使用连发(每个请求10条消息)时 并通过将总经过时间除以总乒乓消息数(总延迟)/(总乒乓接收消息数)来计算经过的延迟时间。 使用rte_ring 7〜10微秒可以获得很好的性能。

我想知道是否有人可以告诉我应该怎么看才能减少RTE_RING的延迟。 因为即使我不使用多个突发,延迟也应该很低。

以下是用于在tx环中添加数据包的客户端代码

    if (rte_ring_enqueue(tx_ring, client_txt) < 0) {
        printf("[user] Failed to send message - message discarded\n");
    } else {
        total_sent++;
        if (chara_debug) printf("[%d] Client txt data::[%.24s...]__length::[%ld]\n", total_sent++, client_txt, strlen(client_txt));
    }

这是用于将数据发送到tx队列的代码

void
l2fwd_tx_loop()
{
    struct rte_mbuf *pkts_burst[MAX_PKT_BURST];
    struct rte_mbuf *m;

    unsigned lcore_id;
    unsigned portid, nb_rx;
    struct lcore_queue_conf *qconf;
    struct rte_eth_dev_tx_buffer *buffer;

    lcore_id = 1;
    qconf = &lcore_queue_conf[lcore_id];

    struct rte_mbuf *rm[10];
    portid = qconf->rx_port_list[0];
    char* data;
    char* send_msg;
    struct message obj;
    struct fuse_message * e = NULL;
    char *msg, *_msg;
    void *__msg;
    int total_tx;


    while (!force_quit) {

        total_tx=0;
        while(total_tx<batch){
                if (rte_ring_dequeue(tx_ring, &__msg) < 0) {
                    usleep(5);
                    // sched_yield();

                    // printf("Failed to recv message - message discarded\n");
                } else {
                    _msg = (char *)__msg;
                    rm[total_tx] = rte_pktmbuf_alloc(test_pktmbuf_pool);
                    data = rte_pktmbuf_append(rm[total_tx], PKT_SIZE*sizeof(char));


                    if(strcmp(hostname,"c3n24")==0) {
                        data += sizeof(struct ether_hdr) - 2; // ASU SERVER
                        l2fwd_mac_updating(rm[total_tx], portid); // ASU SERVER
                    }

                    rte_memcpy(data, _msg, PKT_SIZE*sizeof(char));

                     if(PKT_SIZE==1024) printf("[%d]\n",total_tx);

                    if(chara_debug) printf("[%d] send msg in DPDK: %s",total_tx, _msg);
                    total_tx++;
                    // rte_pktmbuf_dump(stdout, rm[0], 60);
                }
        }


        int rtn = rte_eth_tx_burst(portid, 0, rm, total_tx);
        for(int i=0; i<total_tx; i++) {
            rte_pktmbuf_free(rm[i]);
        }
    }
}

这是从rx队列接收的代码

void
l2fwd_rx_loop() {
    struct rte_mbuf *pkts_burst[MAX_PKT_BURST];
    struct rte_mbuf *m;

    unsigned lcore_id;
    unsigned i, j, portid, nb_rx;
    struct lcore_queue_conf *qconf;
    struct rte_eth_dev_tx_buffer *buffer;

    lcore_id = rte_lcore_id();
    qconf = &lcore_queue_conf[lcore_id];

    struct rte_mbuf *rm[1];

    while (!force_quit) {
        /*
         * Read packet from RX queues
         */
            portid = qconf->rx_port_list[0];
            nb_rx = rte_eth_rx_burst((uint8_t) portid, 0, pkts_burst, MAX_PKT_BURST);

            for (j = 0; j < nb_rx; j++) {
                m = pkts_burst[j];
                int rte_mbuf_packet_length = rte_pktmbuf_pkt_len(m);
                if (rte_mbuf_packet_length == (PKT_SIZE)) {
                    // rte_pktmbuf_dump(stdout, m, 60);
                    if(strcmp(hostname,"c3n24")==0) {
                        // dpdk_pktmbuf_dump(stdout, m, PKT_SIZE, sizeof(struct ether_hdr)-2);
                        dpdk_packet_process(rte_pktmbuf_mtod(m, void * ), PKT_SIZE, sizeof(struct ether_hdr) - 2);
                    }
                }
                rte_pktmbuf_free(m);
            }
    }

}

这是用于从rx-ring接收数据的代码

    while (batched_packets<targ->batch) {
        if (rte_ring_dequeue(rx_ring, &_msg) < 0){
            usleep(5);
            // sched_yield();

        }
        else {
            recv_msg = (char *) _msg;
            if (chara_debug) printf("[%d] Server reply data::[%.24s...]__length::[%ld]\n", batched_packets, recv_msg, strlen(recv_msg));
            total_recved++;
            batched_packets++;
        }
    }

2 个答案:

答案 0 :(得分:1)

1。修复代码中的错误:

此循环可能会引起各种麻烦:

    int rtn = rte_eth_tx_burst(portid, 0, rm, total_tx);
    for (int i = 0; i < total_tx; i++) {
        rte_pktmbuf_free(rm[i]);
    }

rte_eth_tx_burst()释放了缓冲区,因此我们不需要释放它们。我们只需要释放(或重试发送)已传递给rte_eth_tx_burst()的缓冲区(在本例中为total_tx)与{{1 }}。

因此代码应类似于:

rte_eth_tx_burst()

2。 int rtn = rte_eth_tx_burst(portid, 0, rm, total_tx); for (int i = rtn; i < total_tx; i++) { // Loop from rtn, not from 0 rte_pktmbuf_free(rm[i]); } 对您来说太长了

请注意,我们传递给usleep()的参数是最小时间间隔。

完全删除它们以进行确认。如果这是原因,请将usleep() s更改为以下任一值:

  • usleep()-给定CPU上可能的最短暂停时间

  • rte_pause() / rte_delay_ms()-在给定的时间间隔内基本上重复rte_delay_us()

  • rte_pause()-将CPU释放到另一个线程(如果有)。基本上最短的sched_yield()

还要确保所有usleep()都已注释掉或从代码中编译出来了,因为每次printf()调用都会带来巨大的延迟...

答案 1 :(得分:-1)

感谢您的建议,Andriy Berestovskyy (如果您可以根据我的发现最终确定您的专业建议,那么我会将其标记为合适的答案。)

我找到了答案,线程的设计引起了延迟问题。

rte_ring应该一直忙于轮询。

正在计算延迟的线程正在发送带有rte_ring tx的数据包,但同时也从rte_ring rx接收数据包。这是造成延迟的主要原因。

因此,当我实现dpdk时,我需要确保rte_rings不被中断,并始终进行繁忙的轮询。因此,如果我使用rte_ring tx和rte_ring rx,那么我需要使用分别执行tx和rx的两个线程。

如果我显示了所有源代码,Andriy可能会注意到这个问题, 但是我试图尽可能简化源代码,以便可以从更多顾问那里得到答复。

从来没有想到线程的设计会导致这么多的延迟。