Вопрос 1: время выполнения вызова функции

#c

#c

Вопрос:

Если большинство вызовов функции foo(), приведенных ниже, передают одно из 10 конкретных значений, какой метод значительно сократит время выполнения вызова функции?

какие варианты являются правильными? Я думаю, что выбор D, но я не уверен. Эксперты. Мысли?

A. Замените * на проверку блока if-else для 10 значений, соответственно присвоив r
B. Удалите встроенную
C. Удалите foo() и переместите трудоемкую операцию к ее вызывающей стороне
D. Замените * кодом, выполняющим поиск по таблице, с 10 значениями и соответствующими значениями r
E. Замените * на swtich ?

 1 inline int foo (int x) {
2   int r;
3 
4   * // time-consuming operation on x, result stored in r
5 
6   return r;    
7 }
  

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

1. Это похоже на вопрос домашнего задания. Как вы считаете, правильный ответ и почему?

2. Просто добавьте дополнительную новую строку между параметрами.

3. Пожалуйста, поясните, почему вы считаете, что D является правильным выбором.

4. Компиляторы умны и могут оптимизировать любой из неоптимальных случаев до оптимального. Проведите сравнительный анализ каждого из них с включенной оптимизацией. Ответ также может меняться в зависимости от домена x и используемого компилятора — здесь может быть не один правильный ответ.

5. @cdhowie хотите опубликовать ответ? Я боюсь, если вопрос будет закрыт. Также можете ли вы исправить выбор в числах. Теперь они находятся в очереди.

Ответ №1:

B не будет иметь никакого эффекта. inline только подавляет правило одного определения; это не заставляет компилятор встроить функцию.

C вряд ли окажет какое-либо влияние; если компилятор определит, что функция является хорошим кандидатом для встраивания, он это сделает. Встраивание вручную может ухудшить производительность.

Остальные три варианта (A, D и E) могут работать лучше или хуже друг друга в зависимости от многих факторов. Самым важным фактором во всем этом является компилятор. Современные компиляторы очень хороши в оптимизации. A, D и E могут быть тривиально преобразованы друг в друга. Следовательно, все они могут быть такими же быстрыми, как и друг у друга.

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

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


Теперь, когда я покончил с этим, если мы предположим, что все оптимизации компилятора отключены, D, вероятно, будет самым быстрым просто потому, что он не имеет ветвей. Оба A и E связаны с ветвлением, и неудачное предсказание ветвления является дорогостоящим.

Я бы ожидал, что D будет самым быстрым. A и E должны выполняться примерно одинаково.


В моих тестах на gcc с -O3 E оптимизирован для таблицы поиска (например, D), но A остается серией условных переходов. Итак, в этом конкретном тесте D и E оба являются правильным ответом.

Переключаясь на clang с -O3 , он оптимизирует как A, так и E для использования таблицы подстановки (например, D). Он генерирует эквивалентную сборку для всех параметров.

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

1. @Mohammad Также смотрите мою правку. Clang, в частности, способен оптимизировать A и E, чтобы стать D. Поэтому все три параметра эквивалентны clang.

2. @Mohammad Я бы предположил, что отрицательные голоса вызваны тем, что это не ваш вопрос. Вы просто скопировали его откуда-то и добавили то, что составляет дикую догадку (без подтверждающих причин). Это не показывает никаких исследовательских усилий (один из трех критериев для отклонения). Кроме того, это надуманный вопрос, что делает его не особенно полезным (второй критерий для отклонения).

3. @cdhowie Разве не правдоподобно, что выбор трудоемкой операции может повлиять на результаты? Наличие вызовов функций для других единиц перевода, вероятно, повлияло бы на то, насколько компилятор мог бы оптимизировать здесь.