ideas
ideas copied to clipboard
Добавление новых часов и исправление функций сна
В С++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
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/
Большое спасибо за подробный ответ. По vDSO согласен (не обязательно оно, но хотелось бы чтобы самым оптимальным доступным способом). Про "Точность" мой недосмотр (там все хорошо). Остается только добавить более быстрые версии часов (если они доступны на платформе), чтобы можно было получить самые быстрые часы с разрешением не менее ... . sleep_* поисследую
Попробовал разные функции. Вот результаты 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
"Какая именно у вас платформа (версия 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
_COARSE часы быстрее более чем в 100 раз https://quick-bench.com/q/IAaixmnh5wW5_Qe0CdMm4CCV_Pw
Боюсь это предложение я сам не напишу)
Боюсь это предложение я сам не напишу)
Главное начните, а как поймёте что застряли - говорите и скидывайте результат. Это как правило сильно упрощает мне дальнейшую задачу по доработке... а порой и дорабатывать не надо ;-)
@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
В windows тоже возможна реализация быстрых часов. Например std::time (которая возвращает секунды) в windows, работает со скоростью COARSE часов в linux (примечательно то что glibc не использует вызовы gettimeofday и time которые сильно быстрее, а просто обрезает время взятое из clock_gettime до нужной точности)