ideas icon indicating copy to clipboard operation
ideas copied to clipboard

Добавление новых часов и исправление функций сна

Open Roman-Koshelev opened this issue 3 years ago • 10 comments

В С++20 сильно расширили зоопарк часов, но так и не сделали то чего хотелось бы.

Проблемма 1.

Сейчас (до с++20 по крайней мере) есть трое часов (system_clock, steady_clock, high_resolution_clock). system_clock - часы реального времени с разрешением ??? В Windows это 100ns, на linux в libc++ 1us (наносекунды обрезаются) а в libstdc++ 1ns steady_clock - монотонные часы с разрешением обычно 1ns high_resolution_clock - непонятные часы которые steady_clock в Microsoft STL и libc++, и system_clock в libstdc++. Как и для чего их можно использовать абсолютно не ясно.

Проблемы:

  • В linux эти часы зачастую работают не через vDSO (что очень печально).
  • Непонятно как получить разрешение
  • Отсутствуют их быстрые но менее точные версии

Чего хочется:

  • разрешение часов в constexpr
  • обязать их работать через vDSO
  • добавить быстрые версии этих часов (с меньшим разрешением). В linux это CLOCK_REALTIME_COARSE и CLOCK_MONOTONIC_COARSE которые сильно шустрее и имеют размешение 1ms чего достаточно для 99% кейсов
  • интерфейс для получения типа нужных часов в compile time. Например: дай мне самые быстрые steady часы с разрешением не менее 10ms

Проблемма 2.

Для того чтобы заснуть с++ предоставляет 2 функции (sleep_for и sleep_until). Обе работают интересным образом. В Microsoft STL функция sleep_for прибавляет к переданному времени clock::now() и делает sleep_until. libc++ и libstdc++ делают наоборот. sleep_until отнимает от переданного времени clock::now() и вызывает sleep_for

Проблемы Microsoft STL:

  • функция вызывает clock::now() что лишнее действие которое конечно нужно для точности, но не всем. Если пользователь захочет он должен сам вызывать clock::now() и вызывать sleep_until. Если вызывается sleep_for то она должна просто уйти в сон без лишних телодвижений

Проблемы libc++ и libstdc++:

  • тут все сильно хуже. Пользователь дергает sleep_until, вызывается clock::now() (которая к тому же не через vDSO), теряется точность например после clock::now() и до sleep() мы можем получить вытеснение и проспим дольше положенного (разработчики многих либ и продуктов съели на этом сто собак). В итоге мы получаем лишний дорогой системный вызов, за счет уменьшения точности (Как тебе такое Илон Маск?).

Чего хочется:

  • чтобы sleep_until гарантированно работала оптимально
  • чтобы sleep_for просто делала что говорят, и не пыталась чтото улучшить за счет лишних телодвижений

Roman-Koshelev avatar Apr 09 '21 09:04 Roman-Koshelev

@Roman-Koshelev

vDSO

В стандарте C++ не прописываются реализации методов, только их поведение. Это решает две проблемы:

  • на разных платформах разные методы для достижения наибольшей производительности. Например на Windows нет vDSO и соответственно в стандарте придётся прописывать разные имплементации для разных платформ. Это займёт уйму времени
  • тот платформо специфичный метод, что сегодня эффективен, может перестать быть эффективным завтра. Придумают что-то быстрее vDSO, а использовать нельзя будет - в стандарте прописано vDSO..

Поэтому стандарт опирается на предположение, что разработчики компиляторов и стандартных библиотек будут делать самую эффективную реализацию. Это предположение называется Quality of Implementation или QoI

QoI

Не у всех всегда получается с первого раза сделать идеальную имплементацию. При этом разработчики с радостью принимают патчи и фиксы.

Я проверил для своей системы. В libstdc++ есть код https://github.com/gcc-mirror/gcc/blob/16e2427f50c208dfe07d07f18009969502c25dc8/libstdc%2B%2B-v3/src/c%2B%2B11/chrono.cc#L85-L89 , соответственно если не определён макрос _GLIBCXX_USE_CLOCK_GETTIME_SYSCALL то вызов должен идти в vDSO версию функции в glibc https://github.com/lattera/glibc/blob/master/sysdeps/unix/sysv/linux/clock_gettime.c Локальная сборка libstdc++ макрос _GLIBCXX_USE_CLOCK_GETTIME_SYSCALL не определяет:

grep -r '_GLIBCXX_USE_CLOCK_GETTIME_SYSCALL' /data/gcc_build/
/data/gcc_build/stage1-x86_64-pc-linux-gnu/32/libstdc++-v3/include/x86_64-pc-linux-gnu/bits/c++config.h:/* #undef _GLIBCXX_USE_CLOCK_GETTIME_SYSCALL */
/data/gcc_build/stage1-x86_64-pc-linux-gnu/32/libstdc++-v3/config.h:/* #undef _GLIBCXX_USE_CLOCK_GETTIME_SYSCALL */
/data/gcc_build/stage1-x86_64-pc-linux-gnu/libstdc++-v3/include/x86_64-pc-linux-gnu/bits/c++config.h:/* #undef _GLIBCXX_USE_CLOCK_GETTIME_SYSCALL */
/data/gcc_build/stage1-x86_64-pc-linux-gnu/libstdc++-v3/config.h:/* #undef _GLIBCXX_USE_CLOCK_GETTIME_SYSCALL */
/data/gcc_build/x86_64-pc-linux-gnu/32/libstdc++-v3/include/x86_64-pc-linux-gnu/bits/c++config.h:/* #undef _GLIBCXX_USE_CLOCK_GETTIME_SYSCALL */
/data/gcc_build/x86_64-pc-linux-gnu/32/libstdc++-v3/config.h:/* #undef _GLIBCXX_USE_CLOCK_GETTIME_SYSCALL */
/data/gcc_build/x86_64-pc-linux-gnu/libstdc++-v3/include/x86_64-pc-linux-gnu/bits/c++config.h:/* #undef _GLIBCXX_USE_CLOCK_GETTIME_SYSCALL */
/data/gcc_build/x86_64-pc-linux-gnu/libstdc++-v3/config.h:/* #undef _GLIBCXX_USE_CLOCK_GETTIME_SYSCALL */
/data/gcc_build/prev-x86_64-pc-linux-gnu/32/libstdc++-v3/include/x86_64-pc-linux-gnu/bits/c++config.h:/* #undef _GLIBCXX_USE_CLOCK_GETTIME_SYSCALL */
/data/gcc_build/prev-x86_64-pc-linux-gnu/32/libstdc++-v3/config.h:/* #undef _GLIBCXX_USE_CLOCK_GETTIME_SYSCALL */
/data/gcc_build/prev-x86_64-pc-linux-gnu/libstdc++-v3/include/x86_64-pc-linux-gnu/bits/c++config.h:/* #undef _GLIBCXX_USE_CLOCK_GETTIME_SYSCALL */
/data/gcc_build/prev-x86_64-pc-linux-gnu/libstdc++-v3/config.h:/* #undef _GLIBCXX_USE_CLOCK_GETTIME_SYSCALL */

Какая именно у вас платформа (версия C++ библиотеки, ядра ОС, версия C библиотеки)? Заведу им багрепорты, если можно поправить.

Точность

По идее, точность можно достать из std::chrono::*_clock::period http://eel.is/c++draft/time#clock.req Она на вашей платформе показывает истину?

sleep_*

Тут всё чудаковато на разных платформах и процессорах http://gcc.1065356.n8.nabble.com/PATCH-v5-2-8-libstdc-futex-Use-FUTEX-CLOCK-REALTIME-for-wait-tp1692640p1788061.html

Если знаете как улучшить ситуацию - пожалуйста пишите и прикладывайте бенчмарки, оформим фиксы в стандартные библиотеки. Бенчмарки можно набросать на коленке вот тут https://quick-bench.com/

apolukhin avatar Apr 10 '21 08:04 apolukhin

Большое спасибо за подробный ответ. По vDSO согласен (не обязательно оно, но хотелось бы чтобы самым оптимальным доступным способом). Про "Точность" мой недосмотр (там все хорошо). Остается только добавить более быстрые версии часов (если они доступны на платформе), чтобы можно было получить самые быстрые часы с разрешением не менее ... . sleep_* поисследую

Roman-Koshelev avatar Apr 10 '21 17:04 Roman-Koshelev

Попробовал разные функции. Вот результаты nanosleep mean duration 1641857ns realtime_clock_nanosleep mean duration 1065419ns monotonic_clock_nanosleep mean duration 2373212ns realtime_abs_clock_nanosleep mean duration 1155873ns monotonic_abs_clock_nanosleep mean duration 1715776ns sleep_for mean duration 1687494ns

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

Bench - https://yadi.sk/d/PJASVaEtqgZkwg

Roman-Koshelev avatar Apr 10 '21 18:04 Roman-Koshelev

"Какая именно у вас платформа (версия C++ библиотеки, ядра ОС, версия C библиотеки)? Заведу им багрепорты, если можно поправить."

ldd (GNU libc) 2.28 uname -r 4.18.0-193.19.1.el8_2.x86_64 gcc version 7.1.0 x86_64-redhat-linux

Roman-Koshelev avatar Apr 12 '21 07:04 Roman-Koshelev

_COARSE часы быстрее более чем в 100 раз https://quick-bench.com/q/IAaixmnh5wW5_Qe0CdMm4CCV_Pw

image

apolukhin avatar Apr 16 '21 09:04 apolukhin

Боюсь это предложение я сам не напишу)

Roman-Koshelev avatar Apr 16 '21 15:04 Roman-Koshelev

Боюсь это предложение я сам не напишу)

Главное начните, а как поймёте что застряли - говорите и скидывайте результат. Это как правило сильно упрощает мне дальнейшую задачу по доработке... а порой и дорабатывать не надо ;-)

apolukhin avatar Apr 16 '21 19:04 apolukhin

@apolukhin "По идее, точность можно достать из std::chrono::*_clock::period http://eel.is/c++draft/time#clock.req Она на вашей платформе показывает истину?" Нет не показывает. В Microsoft STL std::steady_clock::period == std::nano, хотя на самом деле для получения времени там используются функции _Query_perf_frequency() (говорит сколько тиков в секунде, везде где я проверил это 10^7. Гарантирует что результат постоянен с момента загрузки ПК и до выключения) и _Query_perf_counter() (возвращает те самые тики). Получается что разрешение часов у монотонных часов как и у системных 100ns а не 1 ns

Roman-Koshelev avatar Apr 18 '21 11:04 Roman-Koshelev

В windows тоже возможна реализация быстрых часов. Например std::time (которая возвращает секунды) в windows, работает со скоростью COARSE часов в linux (примечательно то что glibc не использует вызовы gettimeofday и time которые сильно быстрее, а просто обрезает время взятое из clock_gettime до нужной точности)

Roman-Koshelev avatar Apr 18 '21 20:04 Roman-Koshelev