Каков самый быстрый способ приостановить выполнение в c

#c #performance #time

Вопрос:

Я хотел бы приостановить время выполнения в c , и вот код, который я получил:

 while (_run)
{
    //pause code
    std::this_thread::sleep_for(std::chrono::nanoseconds(_interval_ns));

    //doing stuff
    _counter  ;
    (*_mainSrcTick)(GetDeltaTime(), GetName());
    
    for (auto obj : _physicsList)
    {
        PyObject_CallMethod(obj, "PhysicsTick", "(d)", GetDeltaTime());
    }
    
    //used to mesure time
    LastTick = std::chrono::high_resolution_clock::now();
}
 

На всякий случай вот моя функция дельта-времени, которая показывает, сколько тысяч моих интервалов прошло с момента моего последнего тика:

 double difference = std::chrono::duration<double,std::nano>(std::chrono::high_resolution_clock::now() - LastTick).count();
double result = difference/_interval_ns  ;
return resu<
 

Проблема, с которой я сталкиваюсь, заключается в том, что, по-видимому, без паузы мой код может выполняться за пару секунд, а при небольшой паузе даже это занимает миллисекунды. И так далее, я хочу сделать паузу, которая не так сильно нарушает мою программу.

Прежде чем вы зададитесь вопросом, я знаю, как использовать «использование пространства имен», и этот проект работает на x64 (хотя я бы не возражал, если бы он также мог быть запущен на x86), связывает python с c и многопоточен, хотя только на стороне python. Поэтому я предполагаю, что мне нужен более быстрый способ приостановки или, по крайней мере, время, чтобы он мог приостановиться с помощью цикла while. Мне не нужны НИКАКИЕ сторонние зависимости. Я использую VS 2019.

Цель состоит в том, чтобы делать что-то с фиксированной частотой каждого _interval_ns. GetDeltaTime-это то, что я могу указать любые неточности в dostuff, но было бы лучше, если бы частота была точной, а не время дельты. Я ищу микро-секундную точность, и было бы неплохо использовать нано.

Комментарии:

1. В чем заключается ценность _interval_ns ?

2. Зависит, но обычно 1 000 000

3. Использовать felixcloutier.com/x86/umwait если ваш процессор поддерживает это

4. Какова предполагаемая цель задержки по времени? Пример кода заставляет меня подозревать, что вы хотите next_time = get_monotomic_counter() my_delay; while(true) { do_stuff(); delay_until_monotomic_counter(next_time); next_time = my_delay; } , чтобы что do_stuff() -то происходило с фиксированной частотой (например, 60 раз в секунду), даже если do_stuff() требуется неизвестное/переменное количество времени.

5. Да, мне нужно, чтобы «что-то делать» происходило с фиксированной частотой, и я хочу каким-то образом контролировать частоту. Это : next_time = get_monotomic_counter() my_delay; while(true) { do_stuff(); delay_until_monotomic_counter(next_time); next_time = my_delay; } было бы здорово реализовать некоторые способы, но thread::sleep_until это слишком медленно.

Ответ №1:

Когда вы просите свой поток перейти в спящий режим, операционная система не планирует его выполнение, и его переназначение занимает некоторое время. Если вы хотите сделать паузу на очень небольшое количество времени, используйте _mm_pause() . Возможно, вы захотите сделать это фиксированное количество раз, основываясь на ваших собственных измерениях того, сколько времени это занимает в вашей системе. Intel говорит, что на Skylake требуется 140 циклов (на старых процессорах это заняло гораздо меньше времени): https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_pauseamp;expand=5146

Если количество времени, которое вы хотите проспать, невелико, но вас волнует, сколько прошло фактического времени, вы можете проверить время в цикле и позвонить _mm_pause() один раз за цикл. Около 70 нс для clock_gettime() плюс 40 нс для _mm_pause() должно дать вам разрешение где-то около 120 нс на Skylake.

В любом случае вы не будете явно освобождать ядро процессора для использования другими процессами, и ОС может отменить расписание вашего потока, если на том же ядре ожидают запуска другие потоки. Поэтому вам нужно будет настроить сходство с процессором таким образом, чтобы ничто другое не могло работать на том же ядре. Для получения более подробной информации см. https://www.suse.com/support/kb/doc/?id=000017747

См. также: https://software.intel.com/content/www/us/en/develop/articles/benefitting-power-and-performance-sleep-loops.html

Комментарии:

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

2. Что мне следует использовать, чтобы, по вашему мнению, не потребовалось много времени(относительного) для паузы? Я сейчас пробую umwait , но если у вас есть лучший способ, пожалуйста, оставьте ответ @Brendan .

3. Я, кажется, не могу найти много о том, как использовать umwait, поэтому я все еще открыт для ответов.

4. @matapple: umwait-прекрасное предложение, не считая того факта, что он настолько новый, что большинство компьютеров его не поддерживают. Попробуйте _mm_pause() в цикле проверить текущее время, как я предлагал, и посмотрите, делает ли это то, что вам нужно. Если вы можете сообщить нам свои допуски, это также поможет (т. Е. Нужна ли вам точность в 1 микросекунду или что).

5. Это работает, и я получил микросекундное разрешение, которое хотел.

Ответ №2:

Если вы не хотите платить за переключение контекста потока, подумайте о том, чтобы не переключать контекст потока, например, с помощью сопрограмм.Здесь Гор Нишанов утверждает, что приостановка и возобновление сопрограммы занимает менее наносекунды

Комментарии:

1. Операционная система порождает поток, и этот поток имеет контекст, например значения в регистрах процессора. Если у вас больше потоков, чем процессоров, операционная система выделяет каждому потоку некоторое время процессора. Если поток приостанавливается, а затем возобновляется, операционной системе необходимо восстановить контекст этого потока. переключение контекста вики