Вызывает ли оценка короткого замыкания С++ скачки и очистку конвейера ЦП?

AFAIK C++ гарантирует, что в выражении a-expr && b-exprb-expr не оценивается, если a-expr равно false. (Аналогично и для выражений or)

означает ли это, что в сгенерированном коде произойдет скачок, который очистит конвейер процессора? Или как можно/можно это предотвратить?

что, если выражения настолько просты, что глупое вычисление всех результатов будет быстрее, чем короткое замыкание? Может ли это быть обнаружено/использовано компиляторами?

Или программисту придется разделить все выражения на отдельные присваивания переменных и, возможно, использовать вместо них поразрядные логические операторы, чтобы предотвратить такие скачки?

Компилятор работает по правилу «как будто». Он может делать с кодом что угодно, пока наблюдаемое поведение остается прежним. Не беспокойтесь об оптимизации компилятора, если только вы 1) не обнаружите, что ваша программа не соответствует требованиям к скорости, и 2) не профилируете свой код и не обнаружите, что логическое вычисление является узким местом в вашей программе.

Yksisarvinen 16.08.2024 12:26

Ветви не «очищают конвейер» с самого начала, даже если они неправильно предсказаны (что действительно вызывает беспокойство и может иметь значительные затраты, но это не «очистка конвейера»), в любом случае не на соответствующем процессоре.

user555045 16.08.2024 12:27

Вы обеспокоены тем, что могут быть сценарии, в которых bool a = a-expr; bool b = b-expr; bool c= a-expr && b-expr; работает лучше, чем bool c = a-expr && b-expr;? Предположим, что так оно и было, тогда я ожидаю, что авторы компиляторов тоже об этом знают. В любом случае не стоит писать код с учетом таких микрооптимизаций.

463035818_is_not_an_ai 16.08.2024 12:40
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
3
104
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

Самый простой способ для компилятора избежать вычисления b-expr — использовать условные инструкции, отличные от разветвлений. Например. CMOV — «условный ход» на x86. Классический ARM имеет условные варианты практически каждой инструкции.

Но, как вы правильно заметили, оценка b-expr также может быть верным подходом. Составители знают, что происходит как побочный эффект оценки b-expr, и могут увидеть, что эти эффекты отсутствуют или безвредны.

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

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

vlad_tepesch 16.08.2024 16:08

@vlad_tepesch: циклы ЦП довольно дешевы, доступ к памяти часто является более медленной операцией.

MSalters 16.08.2024 16:34
Ответ принят как подходящий

Компиляторы могут конвертировать && в & и || в | на bool, когда стоимость перехода и возможное неправильное предсказание перехода больше, чем стоимость выполнения «бесполезных» вычислений на любом современном оборудовании, это делается только тогда, когда компилятор может сказать, что оценка «потраченной» ветки не имеет побочного эффекта. (по правилу «как если бы»).

возьмем следующие две функции

int foo(int a, int b)
{
    if (a == 1 & b == 2)
    {
        return 1;
    }
    return 0;
}

int bar(int a, int b)
{
    if (a == 1 && b == 2)
    {
        return 1;
    }
    return 0;
}

И gcc, и clang создают один и тот же код для обеих функций в -O1, который не включает ветку, в то время как MSVC, похоже, все еще создает ветвь даже в O2godbolt demo

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

Стандарты C или C++ не гарантируют, что второе выражение не будет вычислено, они гарантируют, что вы не сможете обнаружить разницу.

Например, если гарантировано, что второе выражение не имеет видимых побочных эффектов, если первое выражение ложно, то можно вычислить оба выражения. Или можно было бы оценить части, которые не имеют побочных эффектов. if (x > 0 && a[i++] == 1) может оценить a[i], но должен увеличивать i только в том случае, если x > 0 истинно (если i гарантированно является допустимым индексом).

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

Резюме: нет никаких причин избегать && и || в вашем исходном коде.

Другие вопросы по теме