If my VPS is single core and the server is sys_ticrate 500?KobraFor single core i think betterkernel.sched_latency_ns = 10000000
(10M = 10ms) andsys_ticrate 1000
Generalizing (for standard VDS):kernel.sched_latency_ns = 10000000 / num_core
The HLDS process starts a lot of threads, just runhtop
In general, a client-server architecture network application requires at least 2 cores, the first for the application, the second for processing network interrupts. If the core frequency is very high 4GHz+, I think you can do something similar on one core.
1 / sys_ticrate
) should be a multiple of the running time of the kernel scheduler, determined by the value of kernel.sched_latency_ns
.My default value is 6000000 and the server sys_ticrate is 500. Should I keep the default value?mi3,
The sys_ticrate value cannot be located below 1000, because you will not be able to provide players with 100 network FPS. This value is determined not by the number of cores, but rather by the performance of the server as a whole. In my theory, the inverse of sys_ticrate (1 / sys_ticrate
) should be a multiple of the running time of the kernel scheduler, determined by the value ofkernel.sched_latency_ns
.
I am taking performance measurements in various ranges of sys_ticrate and kernel.schedul_latency_ns values, as soon as I get more empirical data, I will publish an addendum.
Всё потухло?))kto-to, готовлю релиз системы администрирования, от нее и пойдет вся дальнейшая пляска. Модерация заняла некоторое время...
while(true) {
run_server_frame();
thread_sleep(1);
}
kernel.sched_latency_ns * (1 + log2(the number of CPUs))
Чтобы предметно дискутировать нужно определиться с используемыми терминами. Под прерываниями я понимаю аппаратные сигналы, в количественном выражении доступные вПотому как прерывания происходят после КАЖДОГО фрейма.
root# cat /proc/interrupts
Не так. Сетевая карта, в зависимости от аппаратного исполнения, может иметь от 1 до 8 (и более) очередей, отдельно на чтение и запись, каждая из которых управляется прерываниями. Пришел пакет -> Прерывание -> копирование данных пакета из сетевой карты в ОЗУ и так далее по кругу.Вот примерно так это работает. После каждого фрейма поток засыпает и планировщик переключается на другую задачу.
Это тоже неверно. По указанным выше причинам.А "лагать" будет, только если функция run_server_frame будет выполняться дольше 12мс (при условии, что kernel.sched_latency_ns = 12000000).
Это тоже из серии про "колонки", бывают АС90, а бывают газовые. И как это связано с этим:Кроме того, планировщик CFS может увеличивать это значение в зависимости от количества процессоров по такой формуле:
kernel.sched_latency_ns * (1 + log2(the number of CPUs))
while(true) {
run_server_frame();
thread_sleep(1);
}
Именно так. Ваше утверждение не противоречит, а дополняет мой ответ. Да, существуют аппаратные прерывания, помимо софтовых, и да, поток может прерваться в любой момент для обработки данных сетевой карты.Не так. Сетевая карта, в зависимости от аппаратного исполнения, может иметь от 1 до 8 (и более) очередей, отдельно на чтение и запись, каждая из которых управляется прерываниями. Пришел пакет -> Прерывание -> копирование данных пакета из сетевой карты в ОЗУ и так далее по кругу.
Тут вы что-то слишком намудрили. Во первых, планировщик 100мс не будет, так как планировщик будет переключаться на другую задачу после каждого фрейма, когда поток засыпает (псевдокод приводил выше).Лагать будет, когда процесс не успевает обработать прерывания, в нашем случае сетевые, и обработать данные полученные из сетевой карты, в этом случае они будут накапливаться в промежуточном буфере. Таким образом если баланс между временем переключения планировщика задач не будет кратен (или меньше) времени (периода времени) генерируемых прерываний, будет происходить накапливание или потеря пакетов данных, что и будет вызывать задержки или лаги.
На пальцах, планировщик 100мс, прерывания от сетевой карты приходят каждые 20 мс, таким образом к моменту окончания работы процесса в ожидании обработки, в лучшем случае, будет 5 * пакетов, в худшем - они просто "потеряются" (как в нашем случае с UDP-соединениями).
1 игрок генерирует максимально ~100 сетевых пакетов в секунду, умножаем на 32 для полного сервер и получаем суммарно 3200 пакетов в секунду - 0,3мс.
Таким образом мы получаем диаппазон для экспериментов от 0,25 (3200 округляем в большую сторону) мс до 12. Экстрерум "максимальной эффективности" (очень долго объяснять) использования железа на VDS с 3CPU оказался в интервале времени работы планировщика между 4 и 2мс.
Ваше сравнение некорректно. Так как планировщик CFS - это планировщик по умолчанию в ядре Linux. Уверен, у 99% пользователей именно он.Это тоже из серии про "колонки", бывают АС90, а бывают газовые.
На практике не будет такого никогда, дело не в вашем примере, а в механизме работе сетевого стека всех ОС. Также, мы рассматривали идеальный вариант, когда никакого "шума" нет, т.е. мы не учитывали что 99% игровых серверов находятся под перманентными DDOS-атаками и число поступающих пакетов может быть кратно выше установочных величин (3200+++). Поэтому для контроля "переполнения" необходимо использовать другие инструменты, например,планировщик будет переключаться на другую задачу после каждого фрейма, когда поток засыпает (псевдокод приводил выше).
root# ethtool -S eth0
и root# ethtool -с eth0
, поэтому вот это:не жизнеспособно, потому что у нас с ядром по-умолчанию, имели место быть потери, в среднем, 7%.Во вторых, очередь пакетов будет накаливаться если они долго не будут обрабатываться.
Переменная sys_ticrate движка ReHLDS к сетевой подсистеме ядра ОС никакого отношения не имеет, было предположено, что важно чтобы это значение было кратно (делилось без остатка) значению времени работы планировщика. И дело не в том сколько времени будет выполняться функция run_server_frame(), ее работу в любом случае прервет планировщик ядра для обработки поступающего пакета данных. sys_ticrate определяет "плавность" работы внутренних механизмов ReHLDS, который "однопоточный" (это немного не так, но внутренняя очередь событий - линейна) и не умеет распределять нагрузку по ядрам...Слишком маленькое значение sys_ticrate или функция run_server_frame() (из псевдокода) будет выполнятся слишком долго.
В статье и моем предыдущем сообщении именно о сетевых рейтах идет речь.В третьих, нужно уточнить, что количество посылаемых пакетов от клиента к серверу контролируется кваром cl_cmdrate, а не фпс клиента.
Ха-ха... Вы не поняли сути, это не математический расчет, а МОДЕЛЬ в виде потока сознания :-) Которую эмпирически требуется проверить и на основе статистических данных изменять в ту или иную сторону. Скопипастить готовое решение не получится. Нужно немного поработать и пройтись по какому-то диапазону значений от 0,25 до 20мс, соблюдая заданные моделью пропорции с величиной sys_ticrate (с ней тоже можно играться 1-10k), с произвольным шагом, поиграть на полном сервере какое-то время и сделать оценки, не на словах, а на основе каких-то эмпирических данных и поделиться ими... в том числе со мнойвсе расчеты попросту неверны, так как производятся исходя из значения kernel.sched_latency_ns, но это значение в конечном счете может быть другое, в зависимости от количества процессоров.
При высокой "нагрузке" ничего в наших рассуждениях не меняется, а приведенный вами параметр вместе сНе говоря уже о том, что там может быть все еще сложнее и использоваться значение kernel.sched_min_granularity_ns при высокой нагрузке и еще множество других нюансов и факторов.
kernel.sched_wakeup_granularity_ns
заданы в системе на несколько порядков меньше оперируемых нами величин. Я это написал, потому что предположил, что вам не ясен смысл величины O*Log2(N) информацию о которой, вы почерпнули из зарубежных источников. А также почему "кривая" распределения имеет такой странный вид - Log2(N), а также отсутствие понимания того, а будет ли вообще такая величина при, максимум, 6-8 активных процессах в "стандартной" системе VDS продающихся для игровых серверов... И будет ли вообще такой характер распределения для виртуализированных (пара-) систем...Ваше сравнение некорректно.
7. Все выше перечисленное работает исключительно дляя склонен доверять значению sys_ticrate = 1200
pingboost=3
.HZ=250
.Данный код представляет собой функцию NET_Sleep_Timeout(), которая реализует задержку выполнения программы в сетевых операциях. Код отслеживает прошедшее время и количество кадров, чтобы эффективно управлять частотой сетевых запросов.7. Все выше перечисленное работает исключительно дляpingboost=3
.
https://github.com/dreamstalker/reh...a849b3ea94f9ec6/rehlds/engine/net_ws.cpp#L985
DLL_EXPORT int NET_Sleep_Timeout()
{
// Статические переменные для отслеживания времени и количества кадров
static int32 lasttime;
static int numFrames;
static int staggerFrames;
// Получаем частоту кадров из системы
int fps = (int)sys_ticrate.value;
// Получаем текущее время
int32 curtime = (int)Sys_FloatTime();
// Если lasttime не инициализировано, инициализируем его и выходим
if (!lasttime)
{
lasttime = curtime;
return 0;
}
// Если прошла секунда с момента последнего обновления
if (curtime - lasttime > 1)
{
// Обновляем lasttime и сохраняем текущее значение fps
lasttime = curtime;
numFrames = fps;
// Вычисляем staggerFrames для ослабления нагрузки на сеть
staggerFrames = fps / 100 + 1;
}
// Инициализация множества файловых дескрипторов и структуры timeval
fd_set fdset;
FD_ZERO(&fdset);
struct timeval tv;
tv.tv_sec = 0;
// Вычисляем время ожидания в микросекундах на основе частоты кадров
tv.tv_usec = (1000 / fps) * 1000; // TODO: полностью плохой код, исправьте его полностью
// Предотвращаем неположительное время ожидания
if (tv.tv_usec <= 0)
tv.tv_usec = 1;
int res;
// Если numFrames больше 0 и numFrames не делится на staggerFrames
if (numFrames > 0 && numFrames % staggerFrames)
{
SOCKET number = 0;
// Итерируем по сокетам и добавляем их в fdset
for (int sock = 0; sock < NS_MAX; sock++)
{
SOCKET net_socket = ip_sockets[sock];
if (net_socket != INV_SOCK)
{
FD_SET(net_socket, &fdset);
// Находим наибольший сокет
if (number < net_socket)
number = net_socket;
}
#ifdef _WIN32
// Для Windows также обрабатываем IPX сокеты
net_socket = ipx_sockets[sock];
if (net_socket != INV_SOCK)
{
FD_SET(net_socket, &fdset);
// Находим наибольший сокет
if (number < net_socket)
number = net_socket;
}
#endif // _WIN32
}
// Вызываем select для сокетов в fdset с заданным временем ожидания
res = select((int)(number + 1), &fdset, NULL, NULL, &tv);
}
else
{
// Если numFrames меньше или равно 0 или numFrames делится на staggerFrames
// Вызываем select без сокетов, чтобы предотвратить блокировку
res = select(0, NULL, NULL, NULL, &tv);
}
// Уменьшаем numFrames и возвращаем результат select
--numFrames;
return res;
}