JIT против статического компилятора
Как уже было сказано в предыдущих сообщениях, JIT может компилировать IL / байт-код в собственный код во время выполнения. Стоимость этого была названа, но не до конца:
У JIT есть одна серьезная проблема: он не может скомпилировать все: JIT-компиляция требует времени, поэтому JIT будет компилировать только некоторые части кода, тогда как статический компилятор создаст полный собственный двоичный код: для некоторых программ статический компилятор просто легко превзойдет JIT.
Конечно, C # (или Java, или VB) обычно быстрее создает жизнеспособное и надежное решение, чем C ++ (хотя бы потому, что C ++ имеет сложную семантику, а стандартная библиотека C ++, хотя и интересна и мощна, но довольно плохая по сравнению с полной версией. объем стандартной библиотеки из .NET или Java), поэтому обычно разница между C ++ и .NET или Java JIT не будет видна большинству пользователей, а для тех двоичных файлов, которые имеют решающее значение, вы все равно можете вызвать обработку C ++ из C # или Java (даже если такие нативные вызовы могут быть довольно дорогостоящими сами по себе) ...
C ++ метапрограммирование
Обратите внимание, что обычно вы сравниваете код времени выполнения C ++ с его эквивалентом на C # или Java. Но у C ++ есть одна особенность, которая может превзойти Java / C # из коробки, а именно метапрограммирование шаблонов: обработка кода будет выполняться во время компиляции (что значительно увеличивает время компиляции), что приводит к нулевому (или почти нулевому) времени выполнения.
Я пока еще не вижу реального влияния на это (я играл только с концепциями, но к тому времени разница составляла секунды выполнения для JIT и ноль для C ++), но это стоит упомянуть, наряду с тем, что метапрограммирование шаблона не является тривиальна ...
Изменить 2011-06-10: В C ++ игра с типами выполняется во время компиляции, что означает создание универсального кода, который вызывает неуниверсальный код (например, общий синтаксический анализатор от строки до типа T, вызов стандартной библиотеки API для типов T, которые он распознает, и сделать синтаксический анализатор легко расширяемым пользователем) очень просто и очень эффективно, тогда как эквивалент в Java или C # в лучшем случае болезненно писать, и он всегда будет медленнее и разрешается во время выполнения, даже если типы известны во время компиляции, это означает, что ваша единственная надежда - на то, что JIT все это встроит.
...
Изменить 2011-09-20: Команда, стоящая за Blitz ++ ( домашняя страница , Википедия ), пошла по этому пути, и, по-видимому, их цель - достичь производительности FORTRAN в научных расчетах, максимально перемещаясь от выполнения во время выполнения ко времени компиляции с помощью метапрограммирования шаблонов C ++ , Таким образом, часть « Я еще не видел реальный жизненный эффект на это », о котором я писал выше, очевидно, действительно существует в реальной жизни.
Собственное использование памяти C ++
C ++ использует память, отличную от Java / C #, и, следовательно, имеет другие преимущества / недостатки.
Независимо от JIT-оптимизации, ничто не пойдет так быстро, как прямой указатель доступа к памяти (давайте на минутку проигнорируем кеши процессора и т. Д.). Таким образом, если у вас есть непрерывные данные в памяти, доступ к ним через указатели C ++ (то есть указатели C ... Давайте отдадим должное Caesar) будет происходить в разы быстрее, чем в Java / C #. А в C ++ есть RAII, что значительно упрощает обработку, чем в C # или даже в Java. С ++ не нужно using
ограничивать существование своих объектов. А в C ++ нет finally
пункта. Это не ошибка.
:-)
И, несмотря на примитивные структуры C #, объекты C ++ "в стеке" ничего не стоят при распределении и уничтожении, и им не потребуется сборщик мусора для работы в независимом потоке для очистки.
Что касается фрагментации памяти, распределители памяти в 2008 году - это не старые распределители памяти 1980 года, которые обычно сравнивают с GC: распределение C ++ не может быть перемещено в память, правда, но тогда, как в файловой системе Linux: кому нужен жесткий диск дефрагментация, когда фрагментации не происходит? Использование правильного распределителя для правильной задачи должно быть частью набора инструментов разработчика C ++. Теперь написать распределители памяти непросто, и тогда у большинства из нас есть дела поважнее, и для большинства случаев использования RAII или GC более чем достаточно.
Изменить 2011-10-04: Примеры эффективных распределителей: на платформах Windows, начиная с Vista, куча с низкой фрагментацией включена по умолчанию. Для предыдущих версий LFH можно было активировать, вызвав функцию WinAPI HeapSetInformation. ). В других ОС предусмотрены альтернативные распределители (см.https://secure.wikimedia.org/wikipedia/en/wiki/Malloc для списка)
Теперь модель памяти несколько усложняется с появлением многоядерных и многопоточных технологий. В этой области, я полагаю, .NET имеет преимущество, а Java, как мне сказали, занимает верхнюю позицию. Некоторому хакеру, работающему на «голом железе», легко похвалить его код «около машины». Но сейчас гораздо труднее произвести лучшую сборку вручную, чем позволить компилятору выполнять свою работу. Для C ++ компилятор обычно становился лучше, чем хакерский за последние десять лет. Для C # и Java это еще проще.
Тем не менее, новый стандарт C ++ 0x наложит простую модель памяти на компиляторы C ++, которая стандартизирует (и, таким образом, упростит) эффективный код многопроцессорной / параллельной / многопоточной обработки в C ++ и сделает оптимизацию более простой и безопасной для компиляторов. Но потом, через пару лет мы увидим, будут ли выполнены его обещания.
C ++ / CLI против C # / VB.NET
Примечание. В этом разделе я говорю о C ++ / CLI, то есть о C ++, размещенном в .NET, а не о собственном C ++.
На прошлой неделе я прошел тренинг по оптимизации .NET и обнаружил, что статический компилятор в любом случае очень важен. Не менее важно, чем JIT.
Тот же самый код, скомпилированный на C ++ / CLI (или его предке, Managed C ++), может быть в разы быстрее, чем тот же код, созданный на C # (или VB.NET, компилятор которого создает тот же IL, что и C #).
Потому что статический компилятор C ++ был намного лучше для создания уже оптимизированного кода, чем C #.
Например, встраивание функций в .NET ограничено функциями, длина байт-кода которых меньше или равна 32 байтам. Итак, некоторый код на C # создаст 40-байтовый метод доступа, который никогда не будет встроен JIT. Тот же код в C ++ / CLI создаст 20-байтовый метод доступа, который будет встроен JIT.
Другой пример - временные переменные, которые просто компилируются компилятором C ++, но при этом упоминаются в IL, созданном компилятором C #. Оптимизация статической компиляции C ++ приведет к меньшему количеству кода, таким образом, снова разрешит более агрессивную JIT-оптимизацию.
Причиной этого было предположение, что компилятор C ++ / CLI извлек выгоду из обширных методов оптимизации собственного компилятора C ++.
Вывод
Я люблю C ++.
Но, насколько я понимаю, C # или Java в целом лучше. Не потому, что они быстрее, чем C ++, а потому, что, когда вы складываете их качества, они оказываются более продуктивными, нуждаются в меньшем обучении и имеют более полные стандартные библиотеки, чем C ++. Да и у большинства программ разница в скорости (так или иначе) будет незначительной ...
Изменить (2011-06-06)
Мой опыт работы с C # /. NET
Сейчас у меня 5 месяцев почти эксклюзивного профессионального программирования на C # (что дополняет мое резюме, уже полное C ++ и Java, а также немного C ++ / CLI).
Я играл с WinForms (кхм ...) и WCF (круто!) И WPF (круто !!!! Как через XAML, так и через необработанный C #. WPF настолько прост, я считаю, что Swing просто не может сравниться с ним) и C # 4.0.
Вывод таков: хотя легче / быстрее создать код, работающий на C # / Java, чем на C ++, гораздо сложнее создать надежный, безопасный и надежный код на C # (и даже сложнее на Java), чем на C ++. Причин предостаточно, но их можно резюмировать следующим образом:
- Обобщения не так эффективны, как шаблоны ( попробуйте написать эффективный универсальный метод Parse (от строки до T) или эффективный эквивалент boost :: lexical_cast в C #, чтобы понять проблему )
- RAII остается непревзойденным ( GC все еще может протекать (да, мне пришлось справиться с этой проблемой) и будет обрабатывать только память. Даже C #
using
не такой простой и мощный, потому что писать правильные реализации Dispose сложно )
- C #
readonly
и Java final
нигде не так полезны, как C ++const
( вы не можете раскрыть сложные данные только для чтения (например, дерево узлов) в C # без огромной работы, хотя это встроенная функция C ++. Неизменяемые данные - интересное решение , но не все можно сделать неизменяемым, поэтому этого явно недостаточно ).
Итак, C # остается приятным языком, пока вы хотите что-то, что работает, но разочаровывающим языком в тот момент, когда вам нужно что-то, что всегда и безопасно работает.
Java вызывает еще большее разочарование, поскольку имеет те же проблемы, что и C #, и даже больше: из-за отсутствия эквивалента using
ключевого слова C # мой очень опытный коллега потратил слишком много времени, чтобы убедиться, что его ресурсы правильно освобождены, тогда как эквивалент в C ++ имел бы было легко (с использованием деструкторов и умных указателей).
Поэтому я полагаю, что прирост производительности C # / Java заметен для большей части кода ... до того дня, когда вам понадобится сделать код как можно более совершенным. В тот день ты познаешь боль. (вы не поверите, что спрашивают наши серверы и приложения с графическим интерфейсом ...).
О серверной Java и C ++
Я поддерживал связь с серверными командами (я проработал с ними 2 года, прежде чем вернуться в команду GUI) на другой стороне здания, и я узнал кое-что интересное.
В последние годы была тенденция к тому, чтобы серверные приложения Java были предназначены для замены старых серверных приложений C ++, поскольку Java имеет множество фреймворков / инструментов, проста в обслуживании, развертывании и т. Д. И т. Д.
... До тех пор, пока проблема низкой задержки не подняла свою уродливую голову в последние месяцы. Затем серверные приложения Java, вне зависимости от попыток оптимизации, предпринятых нашей опытной командой разработчиков Java, просто проиграли гонку старому, не совсем оптимизированному серверу C ++.
В настоящее время принято решение оставить серверы Java для общего использования там, где производительность, хотя и важна, не зависит от цели с низкой задержкой, и агрессивно оптимизировать и без того более быстрые серверные приложения C ++ для нужд с низкой и сверхнизкой задержкой.
Вывод
Нет ничего проще, чем ожидалось.
Java и даже C # - отличные языки с обширными стандартными библиотеками и фреймворками, где вы можете быстро кодировать и очень скоро получить результат.
Но когда вам нужна чистая мощность, мощная и систематическая оптимизация, сильная поддержка компилятора, мощные языковые функции и абсолютная безопасность, Java и C # затрудняют достижение последнего недостающего, но критического процента качества, вам нужно оставаться выше конкурентов.
Как будто вам нужно меньше времени и менее опытных разработчиков на C # / Java, чем на C ++, чтобы создать код среднего качества, но, с другой стороны, в тот момент, когда вам нужно было отличное или идеальное качество кода, стало внезапно легче и быстрее получить результаты. прямо в C ++.
Конечно, это мое собственное восприятие, возможно, ограниченное нашими конкретными потребностями.
Но, тем не менее, это то, что происходит сегодня, как в командах GUI, так и в командах на стороне сервера.
Конечно, я обновлю этот пост, если произойдет что-то новое.
Изменить (2011-06-22)
«Мы находим, что в отношении производительности C ++ выигрывает с большим отрывом. Однако для этого потребовались также самые обширные усилия по настройке, многие из которых были выполнены на уровне сложности, недоступном среднему программисту.
[...] Версия Java была, вероятно, самой простой в реализации, но трудной для анализа производительности. В частности, эффекты, связанные со сборкой мусора, были сложными и очень трудными для настройки ».
Источники:
Изменить (2011-09-20)
«В Facebook ходят слухи, что« разумно написанный код C ++ просто работает быстро », что подчеркивает огромные усилия, затраченные на оптимизацию кода PHP и Java. Как это ни парадоксально, код на C ++ сложнее написать, чем на других языках, но эффективный код - это намного проще [писать на C ++, чем на других языках] ".
- Херб Саттер в // build / , цитирует Андрея Александреску.
Источники: