В настоящее время я разрабатываю сервер с использованием SCTP, ориентированной на соединение, для обслуживания небольшого числа клиентов. Закончив первый прототип с наивной реализацией, я теперь профилирую приложение для оптимизации. Как выясняется, одним из двух основных потребителей процессорного времени является сетевая часть.
Есть два вопроса об эффективности протокола прикладного уровня, который я реализовал:
1) Размер пакета
В настоящее время я использую максимальный размер пакета 64 байта. Вы можете найти много постов, в которых обсуждаются слишком большие размеры пакетов, но могут ли они быть слишком маленькими? Так как SCTP позволяет мне читать по одному пакету за раз — по аналогии с UPD — при этом гарантируя доставку по порядку — по аналогии с TCP — это значительно упростило реализацию. Однако, если я правильно понимаю, это будет стоить один системный вызов за каждый раз, когда я отправляю пакет. Влияет ли количество системных вызовов на производительность? Смогу ли я сэкономить много циклов ЦП, посылая сообщения в пакетах большими пакетами, то есть 1024 — 8192 байта?
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
}
Первый кажется мне чище, но я нашел много постов, указывающих, что, может быть, второй немного быстрее.
Любые отзывы, конечно, приветствуются.
Насколько я знаю, компиляторы оптимизируют вызовы memcpy, в частности, поэтому вам, вероятно, следует использовать его.
О вашем первом вопросе:
syscall
системный вызов — это ваша ОС, отвечающая или обрабатывающая ваш запрос, и каждый раз, когда ваш запрос выполняется в ядре, это умеренный объем работы.
Если честно, я не знаком с SCTP
По сути, я не занимался программированием сокетов с тех пор, как в последний раз работал над некоторыми вещами и создал сервер через TCP. Я помню, MTU для соответствующего элемента физического уровня был 1500
Я также вспоминаю реализацию моего размера пакета как 1450-1460
так как я пытался получить максимальный размер пакета под 1500
крышка.
Так что я бы сказал, что на вашем месте я бы хотел, чтобы моя ОС была менее активной, чем могла бы, чтобы у меня не возникало проблем с производительностью процессора.
Если вы хотите свести к минимуму количество системных вызовов и отправляете и получаете несколько сообщений одновременно, возможно, вы захотите использовать (только для Linux) sendmmsg()
а также recvmmsg()
.
Чтобы использовать их, вам, скорее всего, придется ставить сообщения в очередь, что может добавить задержку, которой не было бы в противном случае.
Я бы не стал превышать 1024 для размера буфера лично. У меня возникли некоторые проблемы во время выполнения при использовании пакетов более 1500, но 1024 — это, конечно, от 4 до 5 степени, что делает его замечательным в использовании.
Это возможно, но я бы не советовал. Я бы сделал отдельный поток для получения пакетов, используя recvmsg (), чтобы вы могли использовать несколько потоков. Я нашел, что это работает чудесно.
Основной смысл SCTP — это несколько потоков, поэтому я бы в полной мере воспользовался этим. Вы просто должны убедиться, что данные возвращаются в правильном порядке после того, как все получено (что требует определенной работы).