Расчет значения sys_ticrate для VDS

mi3

Сообщения
4
Реакции
0
KobraFor single core i think better kernel.sched_latency_ns = 10000000 (10M = 10ms) and sys_ticrate 1000

Generalizing (for standard VDS): kernel.sched_latency_ns = 10000000 / num_core



The HLDS process starts a lot of threads, just run htop

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.
If my VPS is single core and the server is sys_ticrate 500?
 
Сообщения
226
Реакции
77
Предупреждения
1
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 of kernel.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.
 
Последнее редактирование:

mi3

Сообщения
4
Реакции
0
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 of kernel.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.
My default value is 6000000 and the server sys_ticrate is 500. Should I keep the default value?
 
Сообщения
226
Реакции
77
Предупреждения
1
mi3, try 2 000 000 and 1000, 1 000 000 and 1000 and give feedback on the test results. sys_ticrate 500 - very small for public server (32).
 
Сообщения
313
Реакции
21
Предупреждения
19
Помог
7 раз(а)
Refresh, с правильными пар-ми запуска сервов через скрин скрипты с темой будут? Вроде чета говорил что выложишь скоро 🤨
 
Сообщения
226
Реакции
77
Предупреждения
1
kto-to, готовлю релиз системы администрирования, от нее и пойдет вся дальнейшая пляска. Модерация заняла некоторое время...
 
Сообщения
313
Реакции
21
Предупреждения
19
Помог
7 раз(а)
Refresh, спасибо ✌ Ждемс
 
Сообщения
313
Реакции
21
Предупреждения
19
Помог
7 раз(а)
Refresh, не подскажешь в какие сроки будет статья?(
 
Сообщения
162
Реакции
445
Помог
2 раз(а)
Или я не понял автора, или в этой статье нет смысла. Потому как прерывания происходят после КАЖДОГО фрейма. Псевдокод для наглядности:
C++:
while(true) {
  run_server_frame();
  thread_sleep(1);
}
Вот примерно так это работает. После каждого фрейма поток засыпает и планировщик переключается на другую задачу. А "лагать" будет, только если функция run_server_frame будет выполняться дольше 12мс (при условии, что kernel.sched_latency_ns = 12000000). То есть, если фпс сервера упадет до ~83
Кроме того, планировщик CFS может увеличивать это значение в зависимости от количества процессоров по такой формуле:
kernel.sched_latency_ns * (1 + log2(the number of CPUs))
 
Сообщения
226
Реакции
77
Предупреждения
1
Потому как прерывания происходят после КАЖДОГО фрейма.
Чтобы предметно дискутировать нужно определиться с используемыми терминами. Под прерываниями я понимаю аппаратные сигналы, в количественном выражении доступные в root# cat /proc/interrupts

Вот примерно так это работает. После каждого фрейма поток засыпает и планировщик переключается на другую задачу.
Не так. Сетевая карта, в зависимости от аппаратного исполнения, может иметь от 1 до 8 (и более) очередей, отдельно на чтение и запись, каждая из которых управляется прерываниями. Пришел пакет -> Прерывание -> копирование данных пакета из сетевой карты в ОЗУ и так далее по кругу.

Смысл статьи был в исследовании предположения, что если поток (процесс) выполняется дольше времени периода прерываний от сетевой карты, это приводит к задержкам (лагам) в обработке очереди сетевых пакетов.

Внутриигровые FPS или прерывания никак не связаны с FPS графическими (кадрами), также приведенный вами пример оторван от реальности, поскольку переключение задач может произойти В ЛЮБОЙ момент времени, на любой инструкции процессора, поскольку у каждого процесса (очереди или треда), упрощенно, имеется своя таблица регистров, т.е. для любого процесса это происходит незаметно - и не важно сколько он выполняется, 12мс или 200.

А "лагать" будет, только если функция run_server_frame будет выполняться дольше 12мс (при условии, что kernel.sched_latency_ns = 12000000).
Это тоже неверно. По указанным выше причинам.

Лагать будет, когда процесс не успевает обработать прерывания, в нашем случае сетевые, и обработать данные полученные из сетевой карты, в этом случае они будут накапливаться в промежуточном буфере. Таким образом если баланс между временем переключения планировщика задач не будет кратен (или меньше) времени (периода времени) генерируемых прерываний, будет происходить накапливание или потеря пакетов данных, что и будет вызывать задержки или лаги.

На пальцах, планировщик 100мс, прерывания от сетевой карты приходят каждые 20 мс, таким образом к моменту окончания работы процесса в ожидании обработки, в лучшем случае, будет 5 * пакетов, в худшем - они просто "потеряются" (как в нашем случае с UDP-соединениями).

1 игрок генерирует максимально ~100 сетевых пакетов в секунду, умножаем на 32 для полного сервер и получаем суммарно 3200 пакетов в секунду - 0,3мс.

Таким образом мы получаем диаппазон для экспериментов от 0,25 (3200 округляем в большую сторону) мс до 12. Экстрерум "максимальной эффективности" (очень долго объяснять) использования железа на VDS с 3CPU оказался в интервале времени работы планировщика между 4 и 2мс.

Кроме того, планировщик CFS может увеличивать это значение в зависимости от количества процессоров по такой формуле:
kernel.sched_latency_ns * (1 + log2(the number of CPUs))
Это тоже из серии про "колонки", бывают АС90, а бывают газовые. И как это связано с этим:
Код:
while(true) {
  run_server_frame();
  thread_sleep(1);
}
не понятно...
 
Сообщения
162
Реакции
445
Помог
2 раз(а)
Ну теперь я немного лучше понимаю о чем вы, но...
Не так. Сетевая карта, в зависимости от аппаратного исполнения, может иметь от 1 до 8 (и более) очередей, отдельно на чтение и запись, каждая из которых управляется прерываниями. Пришел пакет -> Прерывание -> копирование данных пакета из сетевой карты в ОЗУ и так далее по кругу.
Именно так. Ваше утверждение не противоречит, а дополняет мой ответ. Да, существуют аппаратные прерывания, помимо софтовых, и да, поток может прерваться в любой момент для обработки данных сетевой карты.
Лагать будет, когда процесс не успевает обработать прерывания, в нашем случае сетевые, и обработать данные полученные из сетевой карты, в этом случае они будут накапливаться в промежуточном буфере. Таким образом если баланс между временем переключения планировщика задач не будет кратен (или меньше) времени (периода времени) генерируемых прерываний, будет происходить накапливание или потеря пакетов данных, что и будет вызывать задержки или лаги.

На пальцах, планировщик 100мс, прерывания от сетевой карты приходят каждые 20 мс, таким образом к моменту окончания работы процесса в ожидании обработки, в лучшем случае, будет 5 * пакетов, в худшем - они просто "потеряются" (как в нашем случае с UDP-соединениями).

1 игрок генерирует максимально ~100 сетевых пакетов в секунду, умножаем на 32 для полного сервер и получаем суммарно 3200 пакетов в секунду - 0,3мс.

Таким образом мы получаем диаппазон для экспериментов от 0,25 (3200 округляем в большую сторону) мс до 12. Экстрерум "максимальной эффективности" (очень долго объяснять) использования железа на VDS с 3CPU оказался в интервале времени работы планировщика между 4 и 2мс.
Тут вы что-то слишком намудрили. Во первых, планировщик 100мс не будет, так как планировщик будет переключаться на другую задачу после каждого фрейма, когда поток засыпает (псевдокод приводил выше).

Во вторых, очередь пакетов будет накаливаться если они долго не будут обрабатываться. То есть, при низком фпс (о графических кадрах речи не идет). Слишком маленькое значение sys_ticrate или функция run_server_frame() (из псевдокода) будет выполнятся слишком долго.
Тут все просто: чем выше фпс, тем чаще сервер обрабатывает пакеты и тем меньше будет очередь пакетов (к слову, в hlds есть поддержка параметра -tos).

В третьих, нужно уточнить, что количество посылаемых пакетов от клиента к серверу контролируется кваром cl_cmdrate, а не фпс клиента. Но да, думаю, количество пакетов не может превышать фпс клиента (хотя, не уверен насчет этого, не проверял).

И наконец, все расчеты попросту неверны, так как производятся исходя из значения kernel.sched_latency_ns, но это значение в конечном счете может быть другое, в зависимости от количества процессоров. Не говоря уже о том, что там может быть все еще сложнее и использоваться значение kernel.sched_min_granularity_ns при высокой нагрузке и еще множество других нюансов и факторов.
Это тоже из серии про "колонки", бывают АС90, а бывают газовые.
Ваше сравнение некорректно. Так как планировщик CFS - это планировщик по умолчанию в ядре Linux. Уверен, у 99% пользователей именно он.
 
Сообщения
226
Реакции
77
Предупреждения
1
планировщик будет переключаться на другую задачу после каждого фрейма, когда поток засыпает (псевдокод приводил выше).
На практике не будет такого никогда, дело не в вашем примере, а в механизме работе сетевого стека всех ОС. Также, мы рассматривали идеальный вариант, когда никакого "шума" нет, т.е. мы не учитывали что 99% игровых серверов находятся под перманентными DDOS-атаками и число поступающих пакетов может быть кратно выше установочных величин (3200+++). Поэтому для контроля "переполнения" необходимо использовать другие инструменты, например, root# ethtool -S eth0 и root# ethtool -с eth0, поэтому вот это:

Во вторых, очередь пакетов будет накаливаться если они долго не будут обрабатываться.
не жизнеспособно, потому что у нас с ядром по-умолчанию, имели место быть потери, в среднем, 7%.

Слишком маленькое значение sys_ticrate или функция run_server_frame() (из псевдокода) будет выполнятся слишком долго.
Переменная sys_ticrate движка ReHLDS к сетевой подсистеме ядра ОС никакого отношения не имеет, было предположено, что важно чтобы это значение было кратно (делилось без остатка) значению времени работы планировщика. И дело не в том сколько времени будет выполняться функция run_server_frame(), ее работу в любом случае прервет планировщик ядра для обработки поступающего пакета данных. sys_ticrate определяет "плавность" работы внутренних механизмов ReHLDS, который "однопоточный" (это немного не так, но внутренняя очередь событий - линейна) и не умеет распределять нагрузку по ядрам...

В третьих, нужно уточнить, что количество посылаемых пакетов от клиента к серверу контролируется кваром cl_cmdrate, а не фпс клиента.
В статье и моем предыдущем сообщении именно о сетевых рейтах идет речь.

все расчеты попросту неверны, так как производятся исходя из значения kernel.sched_latency_ns, но это значение в конечном счете может быть другое, в зависимости от количества процессоров.
Ха-ха... Вы не поняли сути, это не математический расчет, а МОДЕЛЬ в виде потока сознания :-) Которую эмпирически требуется проверить и на основе статистических данных изменять в ту или иную сторону. Скопипастить готовое решение не получится. Нужно немного поработать и пройтись по какому-то диапазону значений от 0,25 до 20мс, соблюдая заданные моделью пропорции с величиной sys_ticrate (с ней тоже можно играться 1-10k), с произвольным шагом, поиграть на полном сервере какое-то время и сделать оценки, не на словах, а на основе каких-то эмпирических данных и поделиться ими... в том числе со мной :smile3:

Не говоря уже о том, что там может быть все еще сложнее и использоваться значение kernel.sched_min_granularity_ns при высокой нагрузке и еще множество других нюансов и факторов.
При высокой "нагрузке" ничего в наших рассуждениях не меняется, а приведенный вами параметр вместе с kernel.sched_wakeup_granularity_ns заданы в системе на несколько порядков меньше оперируемых нами величин. :ok:

Ваше сравнение некорректно.
Я это написал, потому что предположил, что вам не ясен смысл величины O*Log2(N) информацию о которой, вы почерпнули из зарубежных источников. А также почему "кривая" распределения имеет такой странный вид - Log2(N), а также отсутствие понимания того, а будет ли вообще такая величина при, максимум, 6-8 активных процессах в "стандартной" системе VDS продающихся для игровых серверов... И будет ли вообще такой характер распределения для виртуализированных (пара-) систем...

Нет ничего лучше практики :good2:
 
Последнее редактирование:
Сообщения
226
Реакции
77
Предупреждения
1
kto-to, административные барьеры "сообщества" на пути технологического прогресса :dntknw::sorry: я немного остыл и переключился на другие задачи.
 
Сообщения
2,715
Реакции
2,996
Помог
59 раз(а)
1. Введение:
Автор поста ставит перед собой задачу определить оптимальные настройки sys_ticrate для игровых серверов, работающих на виртуальных выделенных серверах (VDS). Основной акцент делается на параметрах планировщика ядра и их влиянии на плавность работы игровых приложений.

2. Исследование планировщика ядра:
Автор подробно рассматривает параметры планировщика ядра, выраженные в наносекундах, и их влияние на переключение между задачами. Обнаружив, что планировщик ядра переключается каждые 12 миллисекунд, автор стремится найти значение sys_ticrate, которое гарантирует, что игровые задачи не будут прерываться.

3. Связь с частотой кадров (FPS):
Автор анализирует связь между sys_ticrate и частотой кадров в секунду (FPS), пытаясь найти оптимальное соотношение. Учитывая стандартные значения FPS, такие, как 100, и предполагаемые параметры железа, автор строит модель, предполагая, что определенное количество прерываний должно соответствовать определенному числу кадров для плавной игровой динамики.

4. Сравнение с другими настройками:
Автор сравнивает свои расчеты с параметрами других серверов, таких как VDS Арены. Обращая внимание на разницу в минимальных значениях FPS и количестве обрабатываемых кадров за период планировщика, автор пытается выявить различия в подходах к настройке sys_ticrate в разных сценариях.

5. Коррекция настроек и оптимизация:
Автор предлагает корректировать параметры ядра, такие как kernel.sched_latency_ns, чтобы оптимизировать соотношение между прерываниями ядра и обработкой кадров. При этом учитываются количество пользователей и необходимое количество прерываний для обработки их запросов, что добавляет дополнительный уровень сложности в определение оптимальных значений.

6. Заключение и результаты тестирования:
Автор приходит к заключению, что оптимальное значение sys_ticrate составляет 1000, учитывая все вышеописанные факторы. После применения этих настроек автор проводит тестирование, чтобы убедиться в правильности своих расчетов и подтвердить, что новые настройки обеспечивают плавную и стабильную работу игровых серверов на VDS. Результаты тестирования могут быть отражены в последующих комментариях или обсуждениях в сообществе.

Спасибо за ваш исчерпывающий анализ! Учитывая ваши расчеты и опыт, я склонен доверять значению sys_ticrate = 1200, которое проверено временем. Важно помнить, что помимо технических параметров, для аргументированных обсуждений производительности также необходимы субъективные метрики. Их отображение, например, с помощью инструментов типа Grafana, может быть весьма полезным для более глубокого понимания работы сервера. Кроме того, оптимизация ядра сервера - ключевой аспект для достижения максимальной производительности. Еще раз спасибо за подробное исследование!
 
Сообщения
1,668
Реакции
1,495
Помог
24 раз(а)
SergeyShorokhov, у нас на дм 300 стояло, ядро любое
 
Сообщения
226
Реакции
77
Предупреждения
1
я склонен доверять значению sys_ticrate = 1200
7. Все выше перечисленное работает исключительно для pingboost=3 .

https://github.com/dreamstalker/reh...a849b3ea94f9ec6/rehlds/engine/net_ws.cpp#L985

Проверять временем это, конечно, хорошо, но математически, по крайней мере мне, очевидно, что 1200 - плохое число, а ближайшее "хорошее" - 1250 :mosking: И это при условии 1CPU и стандартного ядра Linux с параметром ядра HZ=250.

Вы бы поделились с нами своими изысканиями производительности с помощью инструмента Grafanta, который отображает субъективные метрики... И я бы поделился отображениями субъективных метрик с помощью инструмента Zabbix.

Спасибо и вам, за понимание сути происходящего и содержательную дискуссию :good2:
 
Сообщения
2,715
Реакции
2,996
Помог
59 раз(а)
7. Все выше перечисленное работает исключительно для pingboost=3 .
https://github.com/dreamstalker/reh...a849b3ea94f9ec6/rehlds/engine/net_ws.cpp#L985
Данный код представляет собой функцию NET_Sleep_Timeout(), которая реализует задержку выполнения программы в сетевых операциях. Код отслеживает прошедшее время и количество кадров, чтобы эффективно управлять частотой сетевых запросов.

  1. Инициализация и Подготовка:
    • Функция начинает с инициализации статических переменных для отслеживания времени и количества кадров.
    • Получает текущую частоту кадров из системы.
  2. Планирование Задержки:
    • Функция определяет, сколько времени нужно подождать между сетевыми операциями, исходя из текущей частоты кадров.
    • Рассчитывается staggerFrames, чтобы управлять нагрузкой на сеть, основываясь на количестве кадров в секунду.
  3. Ожидание Сетевой Активности:
    • Функция использует системный вызов select, который позволяет ждать активности на сокетах, указанных в fdset, в течение определенного времени.
    • Если numFrames больше 0 и не делится на staggerFrames, функция добавляет сокеты в fdset и ждет событий на этих сокетах в течение определенного времени.
    • Если numFrames равно 0 или делится на staggerFrames, функция вызывает select без сокетов, предотвращая блокировку и позволяя программе ждать указанное время.
  4. Управление Задержкой:
    • После ожидания сетевой активности уменьшается numFrames, чтобы управлять частотой вызовов функции и предотвратить чрезмерные операции ввода/вывода.
    • Функция возвращает результат select, указывая, произошли ли события на сокетах за указанное время или нет.
Код эффективно управляет временем ожидания между сетевыми операциями, учитывая частоту кадров и предотвращая чрезмерные запросы, что позволяет более эффективно использовать ресурсы системы в сетевых приложениях.
C++:
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;
}
Благодарю за интересный обмен и обсуждение кода! Если у вас возникнут еще вопросы или если понадобится помощь в будущем, не стесняйтесь обращаться. Удачи в вашем программировании!
 

Пользователи, просматривающие эту тему

Сейчас на форуме нет ни одного пользователя.
Сверху Снизу