c - 监视服务器的线程性能



>我开发了一个使用 gcc 和 pthreads 的 C 服务器,它接收 UDP 数据包,并根据配置将它们丢弃或转发到特定目标。在某些情况下,这些数据包保持不变,只是重定向,在某些情况下,数据包中的标头被修改,在其他情况下,服务器的另一个模块会修改数据包的每个字节。

要配置此服务器,有一个用 Java 编写的 GUI,它使用 TCP 连接到 C 服务器(以交换配置命令)。可以同时有多个连接的 GUI。

为了测量服务器的利用率,我编写了一个启动两个独立线程(#2和#3)的模块。执行整个转发工作的主线程 本质上的工作方式如下:

struct monitoring_struct data; //contains 2 * uint64_t for start and end time among other fields
for(;;){
recvfrom();
data.start = current_time();
modifyPacket();
sendPacket(); //sometimes to multiple destinations
data.end = current_time();
writeDataToPipe();
}

current_time功能:

//give a timestamp in microsecond precision
uint64_t current_time(void){
struct timespec spec;
clock_gettime(CLOCK_REALTIME, &spec);
uint64_t ts = (uint64_t) ((((double) spec.tv_sec) * 1.0e6) +
(((double) spec.tv_nsec) / 1.0e3));
return ts;
}

如主线程所示,数据结构被写入管道中,线程 #2 在其中等待读取。每次有数据要从管道读取时,线程 #2 都会使用给定的聚合函数,该函数将数据存储在内存中的另一个位置。线程 #3 是一个循环,它始终休眠 ~1 秒,然后发送聚合值(中位数、平均值、最小值、最大值、下四分位数、上四分位数等),然后重置聚合数据。线程 #2 和 #3 由互斥锁同步。

GUI 侦听这些数据(如果监控窗口已打开),这些数据通过 UDP 发送给侦听器(可能有更多),然后 GUI 将数字转换为图表、图形和"压力"指示器。

我想出了这个,因为在我看来,这是对线程 #1 干扰最小的解决方案(假设它在多核系统上运行,它总是这样,并且除了操作系统和 SSH 之外)。

由于性能对我的服务器至关重要(具有更简单配置的版本"1.0"能够管理使用千兆以太网可能的最大流量),我想问一下我的解决方案是否可能不如我认为的那么好,以确保线程 #1 的性能受到的影响最小,如果您认为会有更好的设计?至少我想不出另一种解决方案,它不在数据本身上使用锁(避免管道,但可能锁定线程 #1)或使用 rwlock 的共享列表实现,可能会使读者匮乏。

在某些情况下,数据包更大,但我们目前使用该模式进行性能测量,其中 1 Streams 每秒发送 1000 个数据包。我们目前希望确保 2.0 版本至少可以处理 12 个流(因此每秒 12000 个数据包),但是以前服务器能够管理 84 个流。

将来我想将其他里程碑时间戳添加到线程 #1,例如在modifyPacket()内部(有多个步骤)和sendPacket()之前。

我尝试修改current_time()函数,主要是试图通过存储clock_gettime()的值来删除它以节省时间,但是在我的简单测试程序中,current_time()函数总是击败clock_gettime。

提前感谢您的任何输入。

如果你认为会有更好的设计吗?

简短的回答是使用数据平面开发工具包 (DPDK) 及其设计模式和库。这可能是一个相当长的学习曲线,但就性能而言,它是目前最好的解决方案。它是免费和开源的(BSD许可证)。

更详细的答案:

数据结构写入管道

由于线程 #1 和 #2 是同一进程的线程,因此使用共享内存而不是管道传递数据会快得多。就像您在线程 #2 和 #3 之间使用一样。

线程 #2 使用给定的聚合函数,该函数将数据存储在内存中的另一个位置

这两个线程似乎没有必要。线程 #2 可以读取线程 #1 传递的数据,聚合并发送出去吗?

我想不出另一种解决方案,即不在数据本身上使用锁

看看在 DPDK 中称为"环"的无锁队列。这个想法是在线程之间有一个通用的循环缓冲区,并使用无锁算法将缓冲区排队/取消排队。

我们目前希望确保 2.0 版本至少可以处理 12 个流(因此每秒 12000 个数据包),但是以前服务器能够管理 84 个流。

测量性能并找到瓶颈(似乎您仍然不能 100% 确定代码中的瓶颈是什么)。

仅供参考,英特尔发布 DPDK 的性能报告。L3 转发(即路由)的参考编号高达每秒 3000 万个数据包。

当然,您可能拥有功能较弱的处理器和 NIC,但使用正确的技术可以轻松访问每秒数百万个数据包。

最新更新