前言
接着上篇文章,rte_timer的性能测试https://blog.csdn.net/jacicson1987/article/details/144997298
进行常用的libevent的定时器测试,看看有什么区别,测试方法还是一样,代码放在下面。
测试方法
100万个定时器,设置超时,回调后继续set定时器, 不停循环。
测试代码
#include <stdio.h>
#include <stdint.h>
#include <pthread.h>
#include <signal.h>
#include <unistd.h>
#include <sys/time.h>#include <rte_mempool.h>
#include <rte_timer.h>
#include <rte_eal.h>
#include <rte_launch.h>
#include <rte_cycles.h>
#include <rte_malloc.h>
#include <rte_lcore.h>#include <event2/event.h>
#include <event2/event_struct.h>#define NUM_WORKERS 5
#define NUM_TIMERS_PER_WORKER 1000000
#define TIMER_RATE_PER_SECOND 25000// 定时器参数
#define TIMEOUT_SEC 60 // 超时秒// 定义一个存储定时器的结构体
typedef struct {struct rte_timer timer; // DPDK 定时器struct event timer_event; // libevent 定时器uint64_t id; // 唯一标识符
} timer_data;int quit = 0;#define TIMER_NUM (NUM_WORKERS * NUM_TIMERS_PER_WORKER)struct rte_mempool *timer_mempool;static void signal_handler(int signal) {switch (signal) {case SIGINT:case SIGTERM:printf("\n\nSignal %d received, preparing to exit...\n", signal);quit = true;break;default:break;}
}// 定时器回调函数
static void timer_callback(struct rte_timer *timer, void *arg) {timer_data *data = (timer_data *)arg;// 重设定时器//printf("timer_callback\n");rte_timer_reset(timer, TIMEOUT_SEC * rte_get_timer_hz(), SINGLE, rte_lcore_id(), timer_callback, data);
}static inline int set_timer(int lcore_id)
{int ret = 0;void* td;ret = rte_mempool_get(timer_mempool, &td);if (ret != 0) {printf("Error: get timer from pool failed\n");return -1;}ret = rte_timer_reset(&((timer_data*)td)->timer, TIMEOUT_SEC* rte_get_timer_hz(), SINGLE, lcore_id, timer_callback, td);if (ret != 0) {printf("Error: rte_timer_reset failed %d\n", lcore_id);return -1;}return 0;
}static int set_timers_worker(__rte_unused void *dummy)
{printf("Starting set_timer_worker on core %u\n", rte_lcore_id());struct timeval start_time, end_time;gettimeofday(&start_time, NULL);uint64_t start_tick = rte_rdtsc();int i = 0, ret = 0; int lcore_id = rte_lcore_id();for (i = 0; i < NUM_TIMERS_PER_WORKER; i++) {ret = set_timer(lcore_id);if (ret != 0) {return -1;}usleep(10);}uint64_t end_tick = rte_rdtsc();gettimeofday(&end_time, NULL);double elapsed_time = (end_time.tv_sec - start_time.tv_sec) +(end_time.tv_usec - start_time.tv_usec) / 1000000.0;uint64_t elapsed_tick = end_tick - start_tick;double elapsed_s = (elapsed_tick*1000 / rte_get_timer_hz())/1000.0;printf("Finish set timer on core %u, cost %.3f %.3f\n", rte_lcore_id(), elapsed_time, elapsed_s);while (!quit) {rte_timer_manage();rte_delay_ms(10);}return 0;
}static void ev_timer_callback(evutil_socket_t fd, short event, void *arg)
{timer_data* td = (timer_data*)arg;struct timeval time_out;time_out.tv_sec = TIMEOUT_SEC;time_out.tv_usec = 0;evtimer_add(&td->timer_event, &time_out);//printf("ev_timer_callback\n");
}int ev_set_timer(struct event_base* base)
{int ret = 0;void* td;ret = rte_mempool_get(timer_mempool, &td);if (ret != 0) {printf("Error: get timer from pool failed\n");return -1;}struct event* timer = &((timer_data*)td)->timer_event;evtimer_assign(timer, base, ev_timer_callback, td);struct timeval time_out;time_out.tv_sec = TIMEOUT_SEC;time_out.tv_usec = 0;evtimer_add(timer, &time_out);return 0;
}static int ev_set_timers_worker(__rte_unused void *dummy)
{int i = 0, ret = 0;printf("Starting ev_set_timer_worker on core %u\n", rte_lcore_id());struct event_base* base = event_base_new();struct timeval start_time, end_time;gettimeofday(&start_time, NULL);for (i = 0; i < NUM_TIMERS_PER_WORKER; i++) {ret = ev_set_timer(base);if (ret != 0) {return -1;}usleep(10);}gettimeofday(&end_time, NULL);double elapsed_time = (end_time.tv_sec - start_time.tv_sec) +(end_time.tv_usec - start_time.tv_usec) / 1000000.0;printf("inish set timer on core %u, cost %.3f \n", rte_lcore_id(), elapsed_time);while (!quit) {event_base_loop(base, EVLOOP_NONBLOCK);rte_delay_ms(10);}
}
/*
* command:
* ./timertest -l 1 -n 2 --proc-type=auto
*/
int main(int argc, char *argv[]) {int ret;pthread_t worker_threads[NUM_WORKERS];uint64_t core_id;// 初始化 DPDK 环境ret = rte_eal_init(argc, argv);if (ret < 0) {rte_exit(EXIT_FAILURE, "EAL initialization failed\n");}// 初始化定时器运行环境rte_timer_subsystem_init();// 创建一个内存池来存储定时器数据timer_mempool = rte_mempool_create("timer_mempool", TIMER_NUM, sizeof(timer_data), 0, 0, NULL, NULL, 0, 0, rte_socket_id(), 0);if (timer_mempool == NULL) {rte_exit(EXIT_FAILURE, "Failed to create mempool\n");}//set_timers_worker(NULL);ev_set_timers_worker(NULL);return 0;
}
Makefile
其中libevent使用静态连接,方便后续观察
APP_tool = timertest#SRCS-y = $(wildcard *.c)
SRCS-t = $(wildcard *.c)
PKGCONF ?= pkg-configCFLAGS += -g -O2 $(shell $(PKGCONF) --cflags libdpdk)
LDFLAGS += -Wl,-Bstatic -levent -Wl,-Bdynamic $(shell $(PKGCONF) --libs libdpdk) -lpthread all: $(APP_tool)$(APP_tool): $(SRCS-t)$(CC) $(CFLAGS) $(SRCS-t) -o $@ $(LDFLAGS).PHONY: clean
clean:rm $(APP_tool)
测试步骤
先启动进程,等待100万定时器插入完成
root@r750-132:/home/ckun/ws/rte_timer# ./timertest -l 1 -n 2 --proc-type=auto
EAL: Detected 32 lcore(s)
EAL: Detected 2 NUMA nodes
EAL: Auto-detected process type: PRIMARY
EAL: Detected shared linkage of DPDK
EAL: Multi-process socket /var/run/dpdk/rte/mp_socket
EAL: Selected IOVA mode 'PA'
EAL: No available hugepages reported in hugepages-1048576kB
EAL: Probing VFIO support...
EAL: VFIO support initialized
EAL: Probe PCI driver: mlx5_pci (15b3:1017) device: 0000:98:00.0 (socket 1)
EAL: Probe PCI driver: mlx5_pci (15b3:1017) device: 0000:98:00.1 (socket 1)
EAL: No legacy callbacks, legacy socket not created
Starting ev_set_timer_worker on core 1
inish set timer on core 1, cost 62.502
这里插入100万个定时器时间与DPDK rte_timer相似
使用perf和FlameGraph生成火焰图:
root@r750-132:~/perfs# cat new_svg.sh
#!/bin/bash# 检查是否提供了文件名参数
if [ -z "$1" ]; thenecho "Usage: $0 <filename>"exit 1
fifilename="$1"perf script -i perf.data &> perf.unfold./stackcollapse-perf.pl perf.unfold &> perf.folded./flamegraph.pl perf.folded > "$filename"echo "Flamegraph has been saved to $filename"rm -f perf.unfold perf.folded
root@r750-132:~/perfs#
root@r750-132:~/perfs# perf record -e cpu-clock -g -p 2247392
root@r750-132:~/perfs# ./new_svg.sh perf-ev-10-60-1a.svg
Flamegraph has been saved to perf-ev-10-60-1a.svg
测试结果记录
超时60秒,回调后reset 1秒,轮询间隔 10ms
event_queue_remove_timeout就占到18%,总计libevent花去了20%多的时间。
超时60秒,回调后reset 60秒,轮询间隔10ms
event_queue_remove_timeout 占到0.35% ,总计libevent花去 4.2%的时间。
libevent timer 与 DPDK rte_timer 对比
定时器数量 | 超时间隔 | libevent timer | DPDK rte tiemr |
---|---|---|---|
100万 | 1 秒 | 20% | 10% |
100万 | 60秒 | < 0.4% | 0.42% |
在100万定时器,单线程处理下,在极限1秒的超时情况下,rte_timer比 libevent timer性能明显优势。
但是在通常1分钟的超时的情况下,两者差距不大。