地狱堆栈溢出专家。
我想知道当使用1个突发(tx rx队列)测试往返延迟时,是否有很好的解释说明由rte_ring引起的高延迟。
我已经使用两个节点(客户端和服务器)测试了性能 并计算单次乒乓球往返的经过延迟。 延迟是在两个不同的测试用例上计算的 1.直接从rx tx队列发送和接收的经过的延迟。 2.从rte_ring发送和接收的经过的延迟。
这是我在1个脉冲串(tx rx)上测试DPDK时生成的RTE_RING延迟的结果
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++;
}
}
答案 0 :(得分: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()
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可能会注意到这个问题, 但是我试图尽可能简化源代码,以便可以从更多顾问那里得到答复。
从来没有想到线程的设计会导致这么多的延迟。