Подтвердить что ты не робот

Являются ли ключевые слова оптимизации в C и С++ разумными?

Итак, мы все слышали строку don't-use- register, аргументируя тем, что попытка оптимизировать компилятор - это безумное поручение.

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

Но если мы твердо придерживаемся этого аргумента, не может ли он быть выровнен при каждом ключевом слове с оптимизацией в C? Почему мы используем inline и C99 restrict например?

Я предполагаю, что некоторые вещи, такие как aliasing, выводят некоторые оптимизации hard или даже невозможны, поэтому, когда линия выводится до того, как мы начнем разбираться в Достаточно умный компилятор территория?

Где должна отображаться линия на C и С++ между ложной подачей информации о оптимизации компилятора и предполагающей, что она знает, что она делает?

EDIT: Йенс Густедт указал, что мое объединение C и С++ неверно, поскольку два из ключевых слов имеют семантические различия, а один из них не существует в стандартном С++. У меня была хорошая ссылка о register в С++, которую я добавлю, если найду ее...

4b9b3361

Ответ 1

Я бы согласился, что register и inline несколько похожи в этом отношении. Если компилятор может видеть тело вызываемого абонента при компиляции сайта вызова, он должен иметь возможность принять правильное решение о inlining. Использование ключевого слова inline как в C, так и в С++ имеет больше общего с механикой создания видимого тела функции, чем с чем-либо еще.

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

Ответ 2

inline используется в сценарии, в котором вы реализуете нетемпликационную функцию внутри заголовка, а затем включаете его из нескольких единиц компиляции.

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

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

Ответ 3

register даже не говорит, что вы не можете ссылаться на переменная косвенно (по крайней мере, на С++). Он сказал, что в оригинал C, но это было отброшено.

Не пытайтесь ли вы оптимизировать компилятор - это безумное поручение зависит от оптимизации. Не так много компиляторов, например, преобразует sin(x) * sin(x) + cos(x) * cos(x) в 1.

Сегодня большинство компиляторов игнорируют register, и никто не использует его, потому что компиляторы стали достаточно хорошими в распределении регистров чтобы сделать лучшую работу, чем вы можете с помощью register. По факту, соблюдение register обычно делает сгенерированный код помедленнее. Это не относится к inline или restrict: в в обоих случаях существуют техники, по крайней мере теоретически, что может привести к тому, что компилятор будет работать лучше, чем вы Можно. Однако такие методы не получили широкого распространения и ( как я знаю, по крайней мере), имеют очень высокие накладные затраты на компиляцию, в некоторых случаях время компиляции, которые экспоненциально растут с размер программы (что делает их более или менее непригодными для использования на большинстве реальных программ; время компиляции, которое измеряется в лет действительно неприемлемы).

Что касается того, где рисовать линию... она изменяется во времени. когда Сначала я начал программировать на C, register сделал значительный различие и широко используется. Не сегодня. Я полагаю, что в время, то же самое может произойти с inline или restrict — экспериментальные компиляторы уже очень близки к inline.

Ответ 4

Это вопрос с пламенем, но я все равно погружусь.

Компиляторы намного лучше оптимизируют вашего среднего программиста. Было запрограммировано время на 25 МГц 68030, и я получил некоторое преимущество от использования register, потому что оптимизатор компилятора был настолько беден. Но это было еще в 1990 году.

Я вижу inline так же плохо, как register.

В общем, сначала измерьте, прежде чем изменять. Если вы обнаружите, что код работает так плохо, что вы хотите использовать register или inline, сделайте глубокий вдох, откиньтесь назад и сначала найдите лучший алгоритм.

В последнее время (т.е. последние 5 лет) я прошел через базы кода и удалил функции inline в изобилии, при этом заметные изменения в производительности не были заметны. Однако размер кода всегда выигрывает от удаления методов inline. Это не большая проблема для вашего стандартного многоядерного чуда монстров в стиле х86 в современном возрасте, но это имеет значение, если вы работаете во встроенном пространстве.

Ответ 5

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

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

Таким образом, правило об использовании различных методов оптимизации заключается в том, чтобы спросить, может ли я знать, что компилятор не будет делать лучшую работу здесь? Могу ли я согласиться на это какое-то время - будет ли компилятор оставаться стабильным, пока этот код используется, могу ли я переписать код, когда компилятор изменит эту ситуацию? Как правило, вы должны быть опытным и знающим инженером программного обеспечения, чтобы знать, когда вы можете сделать лучше, чем компилятор. Это также помогает, если вы можете поговорить с разработчиками компилятора.

Это означает, что люди не могут дать вам ответ, в котором есть определенный ориентир. Это зависит от того, какой компилятор вы используете, каков ваш проект, каковы ваши ресурсы и каковы ваши цели и т.д.

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

Ответ 6

Одна вещь, о которой не упоминалось, заключается в том, что многие компиляторы, отличные от x86, не так хороши в оптимизации, как gcc и другие "современные" C-компиляторы.

Например, компиляторы для ПОС абсолютно ужасны при оптимизации. Кроме того, оптимизатор для cicc (CUDA), хотя и намного лучше, по-прежнему, похоже, не хватает довольно простых оптимизаций.

В этих случаях я нашел подсказки оптимизации, такие как register, inline и #pragma unroll, чтобы быть чрезвычайно полезными.

Ответ 7

Разница заключается в следующем:

  • register - это очень локальная оптимизация (т.е. внутри одной функции). Распределение регистров является относительно решенной задачей как с помощью более умных компиляторов, так и с большим количеством регистров (в основном первое, но говорят, что x86-64 имеют больше регистров, чем x86, и оба имеют большее число, а затем говорят 8-битный процессор)
  • inline сложнее, так как это межпроцедурная оптимизация. Однако, поскольку он предполагает относительно небольшую глубину рекурсии и небольшое количество процедур (если встроенная процедура слишком велика, нет смысла вставлять ее), ее можно безопасно оставить компилятору.
  • restrict намного сложнее. Чтобы полностью узнать, что два указателя не являются псевдонимами, вам нужно будет анализировать всю программу (включая библиотеки, систему, плагины и т.д.), - и даже тогда столкнуться с проблемами. Однако для программиста информация более понятна, и это часть спецификации.

Рассмотрим очень простой код:

void my_memcpy(void *dst, const void *src, size_t size) {
    for (size_t i = 0; i < size; i++) {
        ((char *)dst)[i] = ((const char *)str)[i];
    }
}

Есть ли смысл сделать этот код эффективным? Да - memcpy имеют тенденцию быть очень полезными (скажем, для копирования GC). Может ли этот код быть векторизованным (здесь - перемещено словами - скажем 128b вместо 8b)? Компилятор должен был бы сделать вывод, что dst и src никак не псевдонимы, а отмеченные ими регионы независимы. size может зависеть от пользовательского ввода или поведения во время выполнения или других элементов, что делает анализ практически невозможным - аналогичные проблемы с проблемой остановки - в общем случае мы не можем анализировать все, не запуская его. Или это может быть частью библиотеки C (я предполагаю общие библиотеки) и вызывается программой, поэтому во время компиляции все сайты вызовов даже не известны. Без такого анализа программа будет демонстрировать другое поведение с оптимизацией. С другой стороны, программист может убедиться, что они представляют собой разные объекты, просто зная (даже более высокий уровень) дизайн вместо необходимости анализа снизу вверх.

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

Итак, чтобы подвести итог - достаточно, чтобы Smart Compiler не смог вывести restrict (если мы не перейдем к компиляторам, не понимающим смысл кода), не зная всей программы. Даже тогда это было бы близко к неразрешимости. Однако для локальной оптимизации компиляторы уже достаточно умны. Мое предположение, что "Достаточно умный компилятор" со всем программным анализом мог бы вывести во многих интересных случаях.

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

Ответ 8

Из того, что я видел в те дни, когда я больше занимался C/С++, это просто заказы, непосредственно переданные компилятору. Компилятор может попытаться встроить функцию, даже если для этого не требуется прямой порядок. Это действительно зависит от компилятора и может даже вызвать некоторые проблемы с перекрестными компиляторами. Например, визуальная студия обеспечивает различные уровни оптимизации, которые соответствуют разным уровням интеллекта компилятора. Я прочитал, что все функции класса неявно встроены, чтобы дать компилятору подсказку для минимизации служебных вызовов функции. В любом случае эти директивы чрезвычайно полезны, когда вы используете менее интеллектуальный компилятор, в то время как в интеллектуальных случаях они могут быть очень очевидны для компилятора, чтобы сделать некоторую оптимизацию.

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

Компиляторы, такие как g++, в наши дни очень хорошо оптимизируют код. Вы можете также искать оптимизацию в другом месте, возможно, в методах и алгоритме, которые вы используете, или с помощью TBB или CUDA, чтобы сделать ваш код параллельным.