Обновить
19
0
Отправить сообщение

Собственно обновил статью с измерением при помощи осциллографа

ну когда у нас ядра растут как на дрожжах это уже не является проблемой

В этом и вся суть - то что для вас не является проблемой, для нас является. Сценарии есть разные, я же не заставляю везде использовать предложенный таймер :)
К примеру, наш продукт состоит кучи процессов-сервисов большинство из которых содержит таймер, обычно по несколько процессов на машине, в вырожденных случаях 10 и более. Ну а машины - не наши, есть и домашние пользователи с 4 ядрами и даже двухьядерники попадаются на вспомогательных машинах.
И как раз недавно в таймере был баг который приводил к тому что он крутил очень много нопов - в итоге машину только с кнопки можно было перезагрузить, глухое зависание
Предложенный таймер прекрасно справлялся с задачей на том же железе пока я туда не влез и не поломал, ни до ни после жалоб не было.

ну использовать недокументироыванные функции это тоже не решение. Завтра выйдет обновление и досвидос.

Тут стоит понимать что "недокументированные функции" тоже бывают разными. NtDelayExecution существует с доисторических времен (не удивлюсь если аналог был ещё на OS/2), это KeDelayExecutionThread, другие две функции добавлены в Windows NT 3.5 в 94м году и уже тоже документированы на уровне ядра: ExQueryTimerResolution и ExSetTimerResolution. В целом, я готов поспорить что они никуда не собираются в ближайшем будущем.

поэтому правильное решение все таки только через драйвер идти.

Да, это так. Зря что у меня нету сертификата которым можно этот драйвер подписать :)

и знаю что 10 тактов занимает вызов

Будем честны - не всё так просто

вернее знаю и мне не нравится что там под капотом

В случае с Windows и MSVC это будет QueryPerformanceCounter, который, в случае со свежими версиями Windows и процессором где есть Invariant TSC, будет использовать rdtsc (если хочется проверить лично - смотреть внутрь функции RtlQueryPerformanceCounter).
В случае с Linux логично предположить что будет использоваться clock_gettime с параметром CLOCK_MONOTONIC который, совершенно внезапно, точно так же использует rdtsc в случае если процессор поддерживает Invariant TSC.

Собственно проблема в том что пока ядро исполняет нопы - оно не исполняет ничего полезного (а могло бы), поэтому уступить квант времени - априори оптимальнее.

Далее, если уж писать на плюсах то всю работу с rdtsc можно заменить на вызовы std::chrono::steady_clock::now() (номинально std::chrono::high_resolution_clock::now(), но учитывая "нюансы с реализациями" steady_clock будет более переносимым решением).

Ну и, наверное, стоит уточнить что вся суть моей статьи - это как сделать таймер на 1мс не скатываясь до цикла с нопами. Цикл с нопами работает, и он у меня в коде тоже есть, но цель - крутить нопы только в случае крайней необходимости.

Разница в том что реалтаймовое ядро дает гарантии а тут... "как получится".

Измерить извне я могу попробовать но мне банально некуда воткнуть щуп осцилографа - нужна железка, быстрая, без буфера, без регистрации и без смс. Разве что взять USB-COM адаптер и попробовать на нём сгенерировать 500Гц меандр и посмотреть что с этого получится

У нас вроде как работает уже пару лет, у клиентов есть Win7 машины. Но конкретно таймер в 1мс без гарантий - у нас этот же код крутит 10мс таймер повышенной точности.

Ну вот первоначальная статья про 1мс тайминг посвящена именно проблемам с MIDI - насколько я понял, в Windows при выводе MIDI нет нормальной буферизации и нужно софтварно реализовать тайминг.

Далеко не у всех нынче есть COM порт - у меня нет :)
Но, кстати говоря, есть у меня один pet проект (даталоггер для блока управления двигателем) где используется COM через USB - когда в следующий раз буду ковыряться в нём с осцилографом то попробую.

Замечание верное, но, откровенно говоря, для промежутков в 1мс (или даже 0.1мс) у современных машин на Windows средства измерения с огромнейшим избытком - измерить (и проконтролировать) можно, а вот уснуть на малый промежуток - нечем. Собственно, к решению задачи изначально подход с предположением что хардварный счетчик который используется ОС имеет запас в несколько порядков, конкретно на моей машине Windows репортит точность в 100ns и дальше исходим из доверия к этому счетчику.

Это в статье по ссылке в начале есть - вариант "while цикл", и, собственно, вся сложность и состоит в том чтобы поток не занимал ядро на 100%

В целом это решение на жесткое реальное время не претендует - в случае повышенной нагрузки на систему оно уплывёт. У меня есть вариант посложнее с комленсациями но всё-таки Windows - не RTOS, а C# так и подавно, приходит Garbage Collection и можно гасить свет.

Тем что это решение не решает проблему вызова функции раз в 1 мс а просто получает текущее значение счетчика циклов процессора, поведение которого зависит от конкретной модели процессора.

А вот задача "измерить точное время" это совершенно иная история и по состоянию на сегодня она уже успешно решается стандартными библиотеками - в C# это Stopwatch.StartNew(), в случае с C++ это std::chrono::steady_clock::now(), а для C (и других языков) даже без ASM в Win32 доступен QueryPerformanceCounter

Конкретно 1мс таймер? Это код который не имеет применения... Или имеет :)
У нас в коде используются промежутки 10 мс и более, проблема в том что с приемлемой точностью 10мс отмерить тоже нечем, мультимедиа таймеры точностью не отличились. Т.е. это в первую очередь точный таймер а не быстрый таймер. Просто так случилось что его точности хватает и для промежутков в 1мс и менее, а это уже отличный повод похвалиться успехами, не так ли?

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

Команда не спит, команда просто читает время. В случае с Windows такое доступно при помощи QueryPerformanceCounter

Да, виноват, в рамках примера это наверное было бы лучше чем С-стайл.

У меня дефолтная настройка таймера именно 0.5мс, спасибо WPF, спасибо хрому и (в моем случае) спасибо софту который мониторит напряжения на процессоре.

Номинально начиная с Win10 2004 это должно влиять гораздо меньше но конкретно в нашем продукте работа от батарей не рассматривается как класс, поэтому тему я не исследовал.

Да, но разрешение системного таймера в 1мс сна для устойчивой работы таймера в 1мс - это много. Для таймеров в 3мс и более - вполне множно обойтись документированными функциями.

Я постарался урезать код до минимума, как показывает практика читать длинные простыни не слишком интересно.
Для этого есть alertable wait states, при добавлении нового таймера можно будить поток. Плюс, можно ограничить максимальную длительность сна - даже если всегда спать 0.5мс это не сильно убьет нагрузку на процессор. Ну или ваш вариант :)

По умолчанию в Windows сон потока происходит теми же 15.6мс квантами, так что скорее всего Sleep(1) приведет к засыпанию на 15.6мс. А начиная с Win10 2004 - это прямо гарантировано, если не повысить разрешение системного таймера для своего процесса.

Да - это тот же код, просто с дополнительной оптимизацией на сверхмалые периоды.
При этом надо понимать что всё равно будут вырожденные случаи вроде таймера в 0.45мс где ну точно-точно только while() цикл, или 0.95мс где хочешь не хочешь а надо будет дожигать половину периода при помощи SpinWait с соответствующей нагрузкой на процессор.

Информация

В рейтинге
Не участвует
Откуда
Украина
Зарегистрирован
Активность