我目前正在使用面向连接的SCTP开发服务器来为少数客户端提供服务。在完成第一个原型实现后,我现在正在分析应用程序以进行优化。事实证明,CPU时间的两个主要消费者之一是网络部分。
关于我实施的应用程序级协议的效率,有两个问题:
1)数据包大小
目前,我使用的最大数据包大小为64字节。您可以找到许多帖子讨论数据包大小太大,但它们是否太小?由于SCTP允许我一次读取一个数据包 - 类似于UPD - 同时保证按顺序传送 - 类似于TCP - 这显着简化了实现。但是,如果我理解正确的话,每次发送数据包时都会花费一个系统调用。系统调用的数量是否会对性能产生重大影响?通过以更大的数据包(即1024 - 8192字节)串行发送消息,我是否能够节省大量的CPU周期?
2)读取和写入缓冲区
我目前正在使用memcpy将数据移入和移出应用程序级网络缓冲区。我发现很多关于什么是更高效,memcpy或正常分配的相互矛盾的帖子。我想知道在这种情况下,一种方法是否会明显快于另一种方法:
选项1
void Network::ReceivePacket(char* packet)
{
uint8_t param1;
uint16_t param2
uint32_t param3;
memcpy(¶m1, packet, 1);
memcpy(¶m2, packet+1, 2);
memcpy(¶m3, packet+3, 4);
// Handle the packet here
}
void Network::SendPacket(uint8_t param1, uint16_t param2, uint32_t param3)
{
char packet[7]
memcpy(&packet, ¶m1, 1);
memcpy(&packet+1, ¶m2, 2);
memcpy(&packet+3, ¶m3, 4);
// Send the packet here
}
选项2
void Network::ReceivePacket(char* packet)
{
uint8_t param1;
uint16_t param2
uint32_t param3;
param1 = *((uint8_t*)packet);
param2 = *((uint16_t*)packet+1);
param3 = *((uint32_t*)packet+3);
// Handle the packet here
}
void Network::SendPacket(uint8_t param1, uint16_t param2, uint32_t param3)
{
char packet[7]
*((uint8_t*)packet) = param1;
*((uint16_t*)packet+1) = param2;
*((uint32_t*)packet+3) = param3;
// Send the packet here
}
第一个似乎对我来说更清洁,但我发现很多帖子表明第二个可能要快得多。
当然欢迎任何形式的反馈。
答案 0 :(得分:0)
据我所知,编译器特别优化了memcpy调用,所以你应该使用它。
关于你的第一个问题:
系统调用syscall
是您的操作系统回复或处理您的请求以及每次在内核中执行请求时,这是一项适度的工作。
说实话,我不熟悉SCTP
概念,事实上,自从我上次处理一些东西并通过TCP创建服务器以来,我还没有处理套接字编程。我记得相关物理层元素的MTU是1500
,我还记得将我的数据包大小实现为1450-1460
,因为我试图在1500
上限下方获得最大数据包大小。
所以我说的是如果我是你,我会希望我的操作系统不那么活跃,因此我不会遇到任何CPU性能问题。
答案 1 :(得分:0)
如果您希望最大限度地减少系统调用次数,并且确实发现自己一次发送和接收多条消息,则可能需要查看(仅限Linux)sendmmsg()
和{{ 3}}
要使用这些,您可能需要在内部对消息进行排队,这可能会增加其他情况下不会存在的延迟。
答案 2 :(得分:0)
我个人不会超过1024的缓冲区大小。我在使用超过1500的数据包时遇到了一些运行时问题,但1024当然是4到5的功率使它很好用。 这是可能的,但我不建议。我会使用recvmsg()创建一个单独的线程来接收数据包,这样你就可以使用多个流。我发现这个工作很精彩。 SCTP的要点是多个流,所以我会充分利用它。一旦收到所有内容,您就必须确保数据以正确的顺序放回(这需要做一些工作)。