Все операции работают над std::mdspan. В примере std::vector для того, чтобы показать как std::mdspan создать над массивом данных.
Да, std::mdspan не владеющий класс. Посмотрите примеры в proposal, часто нужны операции над частью матрицы и соответственно на практике нужен именно не владеющий класс
Точно так же - значение 65535 в такой интерпретации не является валидным для ushort_16.
В случае арифметики насыщения, краевые значения (0 и 65535) не надо воспринимать как не валидные. Это просто значения
Пример задачи где такая арифметика хорошо подходит: наполнение резервуара. Есть резервуар на 65535 единиц. Чтобы вылить из него `x`, надо позвать res = std::sub_sat<unsigned short>(res, x), чтобы долить `y` надо позвать res= std::add_sat<unsigned short>(res, y)
С такими операциями насыщения у вас резервуар не будет содержать больше чем он вмещает, а при заполнении нет риска что резервуар случайно "опустеет" из-за переполнения.
`auto i = {42};` уже превратили в ошибку компиляции, разрешили инициализацию агрегатов через круглые скобки, инициализацию атомиков тоже поправили, assert вот тоже поправили (об этом есть в посте).
Многие другие ужасы уже давно были поправлены, как и говорится в выступлении.
Ну и автор доклада активно участвует в комитете. Большинство подобных правок для упрощения базовых вещей языка - его заслуга
Она должна быть на очень хорошем уровне, на уровне BLAS/LAPACK. Авторы предложения заморачивались с тем, чтобы линал был бинарно совместим с эталонными реализациями BLAS/LAPACK. И соответственно, чтобы можно было использовать эталонные реализации напрямую в имплементации стандартных библиотек.
В некоторых странах "лоббирование" является легальным промыслом. И не смотря на все лоббирования на уровне одного гос. учреждения X в шататх, другое гос. учреждение Y в тех же штатах запрещает использовать рекомендуемые X "надёжные" языки в любых жизненно важных проектах.
Пара хранится в map, но она хранится с константным first. Поэтому *d_map.begin() вернёт `const std::pair<const std::string, int>&`, от которого создастся временный объект `std::pair<std::string, int>`, и ссылка именно на него и вернётся из функции.
Лично я люблю небольшие C++ загадки, и пишу из ожидания что и читателю они по душе
В примере всё верно. Атрибут можно применять к переменным (как в примере в посте), так и к параметрам функций:
The attribute-token indeterminate may be applied to the definition of a block variable with automatic storage duration or to a parameter-declaration of a function declaration.
Ретраи с exponential backoff c jitter в userver работают из коробки для клиентов (пример). Retry budget для клиентов сейчас не доступен в опенсорс, но есть возможность вручную выставлять на стороне сервера ограничение на RPS на "ручку"; и ограничение RPS на сервер целиком выставляется автоматически логикой Congestion Control. Плюс во все компоненты (сервер, клиенты, базы данных) проинтегрирован Deadline Propagation
Так что сервис на userver убить ретраями просто так не получится.
В добавок, сейчас есть Congestion Control для Монги (и скоро появится для PostgreSql!). Так что есть автоматика, которая осознаёт что база данных чувствует себя плохо даже без ретраев, и помогает выйти ей из MFS.
P.S.: Постараемся вынести retry budget для клиентов в опенсорс версию, чтобы userver понежнее относился к сервисам-клиентам
Всё верно, нельзя пользоваться никакими сторонними блокирующими примитивами синхронизации и блокирующими операциями из основного таск процессора (тред пула).
Точнее, можно. Но на большой нагрузке это станет узким местом и вам придётся выносить работу с такой библиотекой в отдельный таск процессор (тред пул).
Делается это легко - передаёте нужный таск процессор в utils::Async
P.S.: мы распишем этот момент в документации поподробнее
Чтобы начать что-то многопоточно считать обычно используется utils::Async или AsyncNoSpan, множество задач дожидаются через GetAll или WaitAny. Для более сложных синхронизаций между задачами, созданными через Async*, есть целый набор примитивов синхронизации и многопоточных контейнеров.
Если у вас какая-то особая специфика или возникнут проблемы - говорите, поможем либо здесь, либо в официальном чатике поддержки.
const auto& data = cache.Get()[some_user];
foo(data);
Кажется что всё ОК, и тесты вам покажут что всё ОК. Однако, гард защищающий содержимое от конкурентных обновлений, будет уничтожен на первой строчке, а значит что data потенциально может обновиться в фоне и ссылка начнёт указывать на невалидные данные или недоступную память. И такую проблему фиг осознаешь!
И проблемы случатся уже в проде! Нет, не случатся! Такой код не скомпилируется, сообщение об ошибке попросит вас сохранить гард на стеке:
const auto snapshot = cache.Get();
const auto& data = snapshot[some_user];
foo(data);
Для предотвращения подобных ситуаций есть класс SharedReadablePtr, многие RCU-подобные классы устроены так же (например rcu::ReadablePtr).
В изначальном предложении была возможность создавать разные hazard pointer domain. Однако в прототипе умудрились так соптимизировать, что разные домены уже не давали щутимого улучшения производительности, и их отложили "на потом"
О, и правда! Спасибо, сейчас поправим
Все операции работают над std::mdspan. В примере std::vector для того, чтобы показать как std::mdspan создать над массивом данных.
Да, std::mdspan не владеющий класс. Посмотрите примеры в proposal, часто нужны операции над частью матрицы и соответственно на практике нужен именно не владеющий класс
В случае арифметики насыщения, краевые значения (0 и 65535) не надо воспринимать как не валидные. Это просто значения
Пример задачи где такая арифметика хорошо подходит: наполнение резервуара. Есть резервуар на 65535 единиц. Чтобы вылить из него `x`, надо позвать
res = std::sub_sat<unsigned short>(res, x), чтобы долить `y` надо позватьres= std::add_sat<unsigned short>(res, y)С такими операциями насыщения у вас резервуар не будет содержать больше чем он вмещает, а при заполнении нет риска что резервуар случайно "опустеет" из-за переполнения.
На всякий случай подсвечу, что "ошибочное поведение != ошибка компиляции". На ошибочное поведение компилятор выдаст предупреждение.
Подсвечу это в статье поярче
`auto i = {42};` уже превратили в ошибку компиляции, разрешили инициализацию агрегатов через круглые скобки, инициализацию атомиков тоже поправили, assert вот тоже поправили (об этом есть в посте).
Многие другие ужасы уже давно были поправлены, как и говорится в выступлении.
Ну и автор доклада активно участвует в комитете. Большинство подобных правок для упрощения базовых вещей языка - его заслуга
Она должна быть на очень хорошем уровне, на уровне BLAS/LAPACK. Авторы предложения заморачивались с тем, чтобы линал был бинарно совместим с эталонными реализациями BLAS/LAPACK. И соответственно, чтобы можно было использовать эталонные реализации напрямую в имплементации стандартных библиотек.
Компилятор должен хорошо оптимизировать подобное самостоятельно, даже без особых инструкций.
Получается у компиляторов по разному https://godbolt.org/z/Kfc1KhqKT
Заведу бегрепорты на компиляторы, где можно улучшить
Языков без загадок не существует
В некоторых странах "лоббирование" является легальным промыслом. И не смотря на все лоббирования на уровне одного гос. учреждения X в шататх, другое гос. учреждение Y в тех же штатах запрещает использовать рекомендуемые X "надёжные" языки в любых жизненно важных проектах.
будет warning
Пара хранится в map, но она хранится с константным first. Поэтому *d_map.begin() вернёт `const std::pair<const std::string, int>&`, от которого создастся временный объект `std::pair<std::string, int>`, и ссылка именно на него и вернётся из функции.
Лично я люблю небольшие C++ загадки, и пишу из ожидания что и читателю они по душе
В примере всё верно. Атрибут можно применять к переменным (как в примере в посте), так и к параметрам функций:
Ретраи с exponential backoff c jitter в userver работают из коробки для клиентов (пример). Retry budget для клиентов сейчас не доступен в опенсорс, но есть возможность вручную выставлять на стороне сервера ограничение на RPS на "ручку"; и ограничение RPS на сервер целиком выставляется автоматически логикой Congestion Control. Плюс во все компоненты (сервер, клиенты, базы данных) проинтегрирован Deadline Propagation
Так что сервис на userver убить ретраями просто так не получится.
В добавок, сейчас есть Congestion Control для Монги (и скоро появится для PostgreSql!). Так что есть автоматика, которая осознаёт что база данных чувствует себя плохо даже без ретраев, и помогает выйти ей из MFS.
P.S.: Постараемся вынести retry budget для клиентов в опенсорс версию, чтобы userver понежнее относился к сервисам-клиентам
Всё верно, нельзя пользоваться никакими сторонними блокирующими примитивами синхронизации и блокирующими операциями из основного таск процессора (тред пула).
Точнее, можно. Но на большой нагрузке это станет узким местом и вам придётся выносить работу с такой библиотекой в отдельный таск процессор (тред пул).
Делается это легко - передаёте нужный таск процессор в utils::Async
P.S.: мы распишем этот момент в документации поподробнее
Чтобы начать что-то многопоточно считать обычно используется utils::Async или AsyncNoSpan, множество задач дожидаются через GetAll или WaitAny. Для более сложных синхронизаций между задачами, созданными через Async*, есть целый набор примитивов синхронизации и многопоточных контейнеров.
Если у вас какая-то особая специфика или возникнут проблемы - говорите, поможем либо здесь, либо в официальном чатике поддержки.
Да, применим. У нас многие сервисы именно с таким профилем
Не в приоритете :(
Но если кто-то принесёт PR с поддержкой и тестами, мы с радостью поревьюим, вмержим, а автора поблагодарим!
Вот например код:
Кажется что всё ОК, и тесты вам покажут что всё ОК. Однако, гард защищающий содержимое от конкурентных обновлений, будет уничтожен на первой строчке, а значит что data потенциально может обновиться в фоне и ссылка начнёт указывать на невалидные данные или недоступную память. И такую проблему фиг осознаешь!
И проблемы случатся уже в проде!Нет, не случатся! Такой код не скомпилируется, сообщение об ошибке попросит вас сохранить гард на стеке:Для предотвращения подобных ситуаций есть класс SharedReadablePtr, многие RCU-подобные классы устроены так же (например rcu::ReadablePtr).
Ага, есть у нас такие планы. Ну и документацию с примерами улучшить в этом месте тоже хотим
Спасибо!
А что такое cci ?
В изначальном предложении была возможность создавать разные hazard pointer domain. Однако в прототипе умудрились так соптимизировать, что разные домены уже не давали щутимого улучшения производительности, и их отложили "на потом"