2013-04-12 3 views
22

Итак, мы все слышали, что линия не используется - register, рассуждение заключается в том, что попытка оптимизировать компилятор - это поручение дурака.Являются ли ключевые слова оптимизации в C и C++ разумными?

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

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

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

Куда должна быть нарисована линия на C и C++ между ложной подачей информации о оптимизации компилятора и при условии, что она знает, что она делает?

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

+1

Я мало знаю о C, но в C++ 'inline' скорее используется для обхода ODR. – dyp

+1

«Куда должна быть нарисована линия» - я думаю, что это всегда должно быть с оценкой разобрано. Выберите тот, который дает лучший результат. – 2013-04-12 11:47:25

+2

Вы действительно должны отличать C и C++, здесь они не совпадают со всеми тремя ключевыми словами, которые вы упомянули: 'inline' и' register' имеют другую семантику, а 'ограничение' даже не существует в C++. –

ответ

9

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

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

+0

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

+0

Компиляторы сегодня обычно могут видеть все вызывающие сайты функции, по крайней мере, если вы используете максимальные алгоритмы оптимизации. Однако верно, что местность, подверженная «регистру», намного меньше, и что более важно, что алгоритмы оптимизации распределения регистров теперь хорошо известны и используются универсально, что не относится к оптимизации, связанным с 'inline' или 'restrict'. –

+2

@JamesKanze: Из интереса, какова механика компилятора, способная видеть все сайты вызовов? Скажем, я создаю библиотеку, содержащую функцию с именем 'foo()', и передаю вам файл '.a'. Вы пишете функцию, называемую 'bar()', которая вызывает 'foo()'. Как компилятор знает что-нибудь о 'bar()' при компиляции 'foo()'? – NPE

5

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

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

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

+0

'inline' _can_ будет использоваться для создания только заголовка библиотеки. Тем не менее, только заголовок - серьезный недостаток; он позволяет людям использовать вашу библиотеку на машинах, о которых вы никогда не слышали, и тем более проверять их. –

+2

gnu способ форсирования вложения - это атрибут функции always_inline. –

5

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

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

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

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

+0

Когда вы начали программировать на C, возможно, вы писали код, очень близкий к системе, и писали в среде с одним процессом, где ваша программа была единственной, которая работала на вашем ПК DOS. – CashCow

+0

+1 но я думаю, что нам нужны типы с добавленными смысловыми ограничениями, чтобы выразить «я хочу стандартную функциональность, не оптимизируйте, если это изменит семантику». в основном для g ++. специальный тип с плавающей запятой, который g ++ не может испортить, и где информация о числовых ограничениях всегда надежна и специальные целые типы, которые он не может испортить (при условии отсутствия обертывания) независимо от '-fwrapv' или нет. –

+0

@CashCow Когда я начал программировать на C, у компилятора было только 64 КБ ОЗУ, и диск, на который он прольется, был гибким. Стратегии оптимизации, которые занимают, возможно, 10 мс сегодня, могут занимать минуты. И не все из них были известны: Сети-Ульман был современным, и регистрация раскраски была только что опубликована. –

5

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

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

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

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

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

+0

Измерение сначала перед изменением (и снова после) является данным. Тем не менее, с большинством современных компиляторов разумное использование 'inline' может значительно улучшить ситуацию (после того, как вы определили, что это необходимо для улучшения) с очень небольшим усилием. И я видел пару случаев, когда «ограничение» могло бы привести к улучшению порядка в небольшой критической функции (для выполнения которой может потребоваться до 20 минут). –

+0

Что касается вашего последнего параграфа, то, похоже, существует тенденция злоупотреблять «inline». Преждевременная оптимизация - преждевременная оптимизация, а исходная версия программы не должна содержать никаких встроенных функций. Но это не означает, что при необходимости оптимизации 'inline' является плохим инструментом. Это один из самых дешевых инструментов (с точки зрения человеческих усилий и стоимости считывания кода), которые вы можете использовать. Ключ должен использовать его только при необходимости. –

+1

Семантика 'inline' полезна в C99, поскольку реализация функции может быть создана в блоке перевода. Если компилятор решает * не * встраивать код, ему не нужно реализовывать реалистичные реалистичные реализации в нескольких единицах перевода. Это полезно также для указателей на функции. –

2

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

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

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

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

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

2

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

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

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

2

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

  • 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 указателя наложения. Например, если мы хотим скопировать память из псевдонимов, приведенный выше код неверен.

Так, чтобы подвести итог - достаточно смарт-компилятор не быть в состоянии вывести restrict (если не перейти к составителям understending значения коды) без зная всю программу. Даже тогда это было бы близко к неразрешимости. Однако для local оптимизация компиляторов уже достаточно умна. Мое предположение, что «Достаточно умный компилятор» со всем программным анализом мог бы вывести во многих интересных случаях.

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

0

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

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

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

Смежные вопросы