【发布时间】:2018-09-08 16:39:23
【问题描述】:
我使用 gcc 和 pthreads 开发了一个 C 服务器,它接收 UDP 数据包并根据配置将它们丢弃或转发到特定目标。在某些情况下,这些数据包不受影响,只是被重定向,在某些情况下,数据包中的标头被修改,在其他情况下,服务器的另一个模块会修改数据包的每个字节。
要配置此服务器,有一个用 Java 编写的 GUI,它使用 TCP 连接到 C 服务器(以交换配置命令)。可以同时连接多个 GUI。
为了衡量服务器的利用率,我编写了一种启动两个单独线程(#2 和#3)的模块。完成整个转发工作的主线程(#1)基本上是这样工作的:
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。
提前感谢您的任何意见。
【问题讨论】:
标签: c multithreading performance pipe monitoring