Существуют ли какие-либо механизмы, отличные от копирования elision, которые могут оптимизировать чрезмерную конструкцию в C ?

#c #constructor #compiler-optimization

#c #конструктор #оптимизация компилятора

Вопрос:

Моя функция имеет оператор switch-case. Существует объект, который используется только в некоторых его ветвях. Могу ли я поместить инициализацию этого объекта перед оператором switch-case и ожидать, что он будет создан только тогда, когда он фактически используется.

Под этим я подразумеваю, есть ли какая-либо разница в производительности между этим кодом:

 void someFunc( SomeEnum someEnum, Parameter parameter )
{
    Object object( parameter );
    
    switch( someEnum )
    {
    case SomeEnum::A:
        otherFunc( object );
        break;
        
    case SomeEnum::B:
        otherFunc( object );
        break;
        
    case SomeEnum::C:
        anotherFunc();
        break;
    }
}
  

И этот:

 void someFunc( SomeEnum someEnum, Parameter parameter )
{   
    switch( someEnum )
    {
    case SomeEnum::A: {
            Object object( parameter );
            otherFunc( object );
        }
        break;
        
    case SomeEnum::B: {
            Object object( parameter );
            otherFunc( object );
        }
        break;
        
    case SomeEnum::C:
        anotherFunc();
        break;
    }
}
  

Комментарии:

1. правило «как если бы»… но тогда Object object( parameter) не должно быть побочного эффекта.

2. вы определили, что это узкое место?

3. @Jarod42 Извините, пропустил эту часть. Исправлено.

4. @bolov да, в значительной степени. Я не делал бенчмарки, но это будет повторяющийся шаблон в куче очень часто выполняемых методов.

5. @Forthleft: » да, в значительной степени. Я не проводил тесты » Тогда вы не определили , что это узкое место; вы предполагаете , что оно может быть одним из них.

Ответ №1:

Могу ли я поместить инициализацию этого объекта перед оператором switch-case и ожидать, что он будет создан только тогда, когда он фактически используется.

Нет. В стандарте C нет механизма, который позволил бы создавать объект, указанный в тексте, который должен быть создан, только фактически создаваться на основе информации о времени выполнения.

Теперь, если Object это тривиально, или если компилятор может видеть, что Object конструктор / деструктор не имеет видимых побочных эффектов за пределами манипулирования состоянием этого объекта, тогда компилятор мог бы в соответствии с правилом «как если бы» эффективно преобразовать ваш первый код во второй код. Но это только потому, что ничто не может проверить, был ли объект создан или не был.

Обратите внимание, что «видимые побочные эффекты» включают такие вещи, как вызов функций выделения памяти, открытие файлов или другие тяжелые действия на уровне операционной системы. IE: именно те вещи, которые вас, вероятно, особенно беспокоили бы с точки зрения производительности. Итак, если это действительно будет иметь значение для производительности, то велика вероятность, что компиляторы не смогут «как-будто» это убрать.

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

Ответ №2:

Это зависит как от компилятора, так и Object . В первом примере плохой компилятор, такой как msvc, фактически выделит меньше места в стеке, а во втором — в N раз больше места в стеке. Но если конструктор Object выполняет некоторую тяжелую работу и / или динамически выделяет много памяти, то последнее было бы предпочтительнее, поскольку конструктор будет вызван только при достижении case .

Комментарии:

1. «плохой компилятор, такой как msvc» Хм… Я критикую MSVC за отсутствие соответствия C и отставание в прошлом в реализации новых стандартных функций, но даже игнорируя то, как далеко они продвинулись сейчас и как они даже возглавляют некоторые из новых функций C , называть это «плохим компилятором» — это слишком. У вас есть какие-либо источники для этого?

2. Я бы процитировал его исходный код; но это закрытый исходный код, поэтому я не могу. Кроме того, я предлагаю вам проверить мой недавний вопрос здесь, на SO. Это живой пример того, почему следует отдавать предпочтение компиляторам вроде clang.

3. @Hi-IloveSO: Я просмотрел этот вопрос, и не было установлено, каков правильный ответ даже в этом случае («правильный», как в том, что, по словам стандарта, должно произойти. Я недостаточно разбираюсь в сложностях шаблонов, чтобы сказать, что говорится в стандарте по этому вопросу). Пока кто-нибудь не взвесит, каким должно быть правильное поведение, этот вопрос вряд ли является доказательством того, что какой-либо компилятор хорош или плох.

4. У всех компиляторов есть ошибка, указывающая на один случай, когда clang прав (и даже это, кажется, не доказательство), на самом деле не имеет значения, поскольку другие также могут указывать на случаи, когда только clang терпит неудачу…

Ответ №3:

Возможно, это потому, что этот пример чрезмерно упрощен, но наличие этого объекта в значительной степени не имеет отношения к работе кода — его значение никогда не используется. Итак, вместо именованной переменной, которая не имеет существенного применения, создайте временную:

 otherFunc(Object());
  

Конечно, это не сработает, если otherFunc предполагается, что можно будет изменить его аргумент.

Другая возможность (опять же, при условии, что otherFunc не изменяет свой аргумент) заключается в использовании аргумента по умолчанию:

 void otherFunc(Object object = Object());
  

Теперь вы можете вызывать его без явного аргумента:

 otherFunc();
  

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