Комментарии 38
Сколько людей думало, что они нужны для других целей?
Автор не берет на себя роль кэпа. Однако статья заставит некоторых задуматься, почему один и тот же код, скомпилированный разными компиляторами, так по-разному может себя вести в плане скорости.
Вы не поверите сколько! :) Ну и плюс те, кто как бы знает, что вроде как оптимизирующие компиляторы должны оптимизировать, но сильно в этом сомневается.
Отсюда всякие умельцы, которые вручную анролят циклы в сорсах, например.
Отсюда всякие умельцы, которые вручную анролят циклы в сорсах, например.
Только ассемблер и ручное высчитывания тактов под линейки процессоров, только хардкор!
Наверное это вкладывается в п.3, но на мой взгляд самое главное это то, что программист может писать понятный код не отвлекаясь на мелкую оптимизацию (простейший пример: ++a или a++).
Данная запись является вводной для цикла постов о принципах работы оптимизирующих компиляторов, о сложностях их использования и о бонусах, которые можно получить от использования хорошего компилятора.
Если вам интересны какие-то определенные темы, оставляйте запросы. Попробуем рассмотреть то, что интересно именно вам.
Если вам интересны какие-то определенные темы, оставляйте запросы. Попробуем рассмотреть то, что интересно именно вам.
Интересно было бы почитать про отличия (чем сильнее — тем лучше) в компиляции для разного железа, с удовольсвием бы почитал в объёме, гораздо большем, чем эта статья.
Интересен обоснованный аргументированный сравнительный анализ компиляторов VC, GDB (+-LLVM). Очень. Был бы благодарен за подобный обзор.
Gdb? Может gcc?
Llvm хорош только тем, что маленький, больше ничем. ИМХО, кончено.
Llvm хорош только тем, что маленький, больше ничем. ИМХО, кончено.
Прошу прощения, жутко затупил, имел ввиду llc, не llvm. Наверно не стоит постить на Хабре в пол пятого ночи.
LLVM ничего так себе компилятор, хоть пока и отстаёт от конкурентов по качеству оптимизации.
LLVM ничего так себе компилятор, хоть пока и отстаёт от конкурентов по качеству оптимизации.
Да, конечно gcc, спасибо!
То то и оно, что у меня, как вероятно и у многих, есть только свое собственное ИМХО на этот счет. А хотелось бы реальных обоснованных цифр. Т.е. что оптимизирует лучше, что хуже, как лучше писать в конкретных случаях и.т.д…
То то и оно, что у меня, как вероятно и у многих, есть только свое собственное ИМХО на этот счет. А хотелось бы реальных обоснованных цифр. Т.е. что оптимизирует лучше, что хуже, как лучше писать в конкретных случаях и.т.д…
НЛО прилетело и опубликовало эту надпись здесь
Почему?
НЛО прилетело и опубликовало эту надпись здесь
Попробую пояснить.
Я не хотел слишком подробно разбирать этот пример, что привело, видимо, к слишком сумбурному тексту.
Дело в том, что мы с вами вкладываем разный смысл в слово «поддерживает».
Конечно же, любой компилятор для C++ поддерживает полиморфизм в том смысле, что строго выполняет требования стандарта языка C++. Но я имел в виду немного другое.
Продолжим разбор примера.
Что происходит в динамике (т.е. во время выполнения программы), когда необходимо выполнить инструкцию «int value = ptr->func();»
А происходит примерно следующее:
1) из участка памяти, соответствующего экземпляру класса, на который указывает «ptr», берется указатель на таблицу виртуальных функций
2) из таблицы виртуальных функций достается указатель на функцию «func»
3) таким образом, мы получили указатель именно на ту версию виртуальной функции «func», которая соответствует конкретному типу класса. Лишь на этом этапе мы можем, наконец, вызвать «func».
Мы видим, что реализованный напрямую механизм виртуальных функций приводит к дополнительным косвенностям: в коде, вместо вызова конкретной функции, стоит вызов функции через указатель. Мало того, что это замедляет сам вызов, это не дает компилятору возможности выполнить подстановку вызова (inline) просто напросто потому, что мы не знаем, какой вызов подставлять (т.к. у нас есть несколько версий функции «func»).
Задача оптимизирующего компилятора состоит в том, чтобы как можно больше узнать о типе класса, для которого вызывается виртуальная функция. А это довольно нетривиальная (в общем случае) задача.
В рассмотренном выше примере компилятор мог действовать примерно так:
1) мы видим, что «ptr» может указывать только на класс типа «A»
2) значит, вызываться может только версия «func», определенная для класса «A»
3) заменяем вызов через таблицу виртуальных функций непосредственно на вызов нужной версии
4) теперь, когда у нас есть конкретный вызов, мы можем его попросту подставить.
Таким образом, у нас вообще не остается вызова. Остается просто присваивание «value = 5».
Я не хотел слишком подробно разбирать этот пример, что привело, видимо, к слишком сумбурному тексту.
Дело в том, что мы с вами вкладываем разный смысл в слово «поддерживает».
Конечно же, любой компилятор для C++ поддерживает полиморфизм в том смысле, что строго выполняет требования стандарта языка C++. Но я имел в виду немного другое.
Продолжим разбор примера.
Что происходит в динамике (т.е. во время выполнения программы), когда необходимо выполнить инструкцию «int value = ptr->func();»
А происходит примерно следующее:
1) из участка памяти, соответствующего экземпляру класса, на который указывает «ptr», берется указатель на таблицу виртуальных функций
2) из таблицы виртуальных функций достается указатель на функцию «func»
3) таким образом, мы получили указатель именно на ту версию виртуальной функции «func», которая соответствует конкретному типу класса. Лишь на этом этапе мы можем, наконец, вызвать «func».
Мы видим, что реализованный напрямую механизм виртуальных функций приводит к дополнительным косвенностям: в коде, вместо вызова конкретной функции, стоит вызов функции через указатель. Мало того, что это замедляет сам вызов, это не дает компилятору возможности выполнить подстановку вызова (inline) просто напросто потому, что мы не знаем, какой вызов подставлять (т.к. у нас есть несколько версий функции «func»).
Задача оптимизирующего компилятора состоит в том, чтобы как можно больше узнать о типе класса, для которого вызывается виртуальная функция. А это довольно нетривиальная (в общем случае) задача.
В рассмотренном выше примере компилятор мог действовать примерно так:
1) мы видим, что «ptr» может указывать только на класс типа «A»
2) значит, вызываться может только версия «func», определенная для класса «A»
3) заменяем вызов через таблицу виртуальных функций непосредственно на вызов нужной версии
4) теперь, когда у нас есть конкретный вызов, мы можем его попросту подставить.
Таким образом, у нас вообще не остается вызова. Остается просто присваивание «value = 5».
Осталось только исправить сумбурно-некорректную фразу в тексте поста и написать рядом «Update», как это принято на Хабре.
НЛО прилетело и опубликовало эту надпись здесь
Все верно: в общем случае такая оптимизация не пройдет. Но в общем случае вообще никакая оптимизация не пройдет. Задача компилятора во многом состоит в том, чтобы отыскивать возможности для оптимизации.
Приведенный пример служит лишь иллюстрацией (одной из самых простых). Но даже такие примеры на практике встречаются довольно часто. Программист редко работает лишь со внешними объектами. Как только он определяет что-то свое, у компилятора сразу возникает простор для творчества.
Приведенный пример служит лишь иллюстрацией (одной из самых простых). Но даже такие примеры на практике встречаются довольно часто. Программист редко работает лишь со внешними объектами. Как только он определяет что-то свое, у компилятора сразу возникает простор для творчества.
На счёт редкого явления — не согласен. После раскрытия шаблонов и инлайнинга сплошь и рядом.
НЛО прилетело и опубликовало эту надпись здесь
Вы, наверное, имеете в виду спецификатор «inline»?
Такой спецификатор является не «указанием», а скорее, пожеланием. Компилятор может отказаться выполнять подстановку функции, отмеченной этим спецификатором.
Помимо этого компилятор может выполнять подстановки функций, не отмеченных словом «inline» (если ему позволяет заданный опциями режим работы).
Так что «инлайнинг» — это действительно оптимизация, и довольно непростая во многих случаях.
Такой спецификатор является не «указанием», а скорее, пожеланием. Компилятор может отказаться выполнять подстановку функции, отмеченной этим спецификатором.
Помимо этого компилятор может выполнять подстановки функций, не отмеченных словом «inline» (если ему позволяет заданный опциями режим работы).
Так что «инлайнинг» — это действительно оптимизация, и довольно непростая во многих случаях.
Тезка, да потому что фраза, приведенная в цитате, как бы нам намекает: «существует как минимум один компилятор С++, который не поддерживает полиморфизм».
НЛО прилетело и опубликовало эту надпись здесь
Что-то я не догадываюсь о ком Вы говорите. Разве есть компиляторы С++ не поддерживающие полиморфизм?
Пожалуйста, см. мой ответ выше для «kaladhara». Я попытался пояснить, что именно я имел в виду под «поддержкой полиморфизма».
Да я и сам не догадываюсь, какой смысл был заложен автором в эту фразу. Не то чтобы я был экспертом в плюсовых компиляторах, так, штук 10 наверное пробовал в разное время, вряд ли больше. Так вот, из них не было ни одного, который бы «не смог понять», какая «версия» виртуальной функции будет вызвана в данном случае.
Кажется, что об очевидном, а интересно, понятные без усиления мозговой деятельности примеры. Спасибо!
Всё так просто и доходчиво, мне кажется, из Вас вышел бы отличным преподаватель информатики в школе. А то, как читаю во многих местах (и по собственному опыту), их качество оставляет желать лучшего…
Всё так просто и доходчиво, мне кажется, из Вас вышел бы отличным преподаватель информатики в школе. А то, как читаю во многих местах (и по собственному опыту), их качество оставляет желать лучшего…
Я думаю, статью стоит не писать общими словами, а взять кусок кода, скомпилировать и показать: вот, интеловский компилятор скомпилил все в 3 инструкции, а gcc к примеру, как всегда, наворотил тонну кода. Как-то так. А так это общий разговор ни о чем.
Кэп работает в Intel?
Зарегистрируйтесь на Хабре, чтобы оставить комментарий
Для чего нужны оптимизирующие компиляторы?