Производительность С++ по сравнению с Java/С#

Я понимаю, что C/С++ создает собственный код для работы на определенной машинной архитектуре. И наоборот, такие языки, как Java и С#, запускаются поверх виртуальной машины, которая абстрагирует оттуда собственную архитектуру. Логически для Java или С# было бы невозможно сопоставить скорость С++ из-за этого промежуточного шага, однако мне сказали, что последние компиляторы ( "горячая точка" ) могут достичь этой скорости или даже превысить ее.

Возможно, это скорее вопрос компилятора, чем вопрос на языке, но может ли кто-нибудь объяснить на простом английском языке, как один из этих языков виртуальных машин может работать лучше, чем родной?

Ответ 1

Как правило, С# и Java могут быть такими же быстрыми или быстрыми, потому что компилятор JIT - компилятор, который компилирует ваш IL при первом запуске, - может сделать оптимизацию, которую скомпилированная программа С++ не может, поскольку она может запрашивать машину. Он может определить, является ли машина Intel или AMD; Pentium 4, Core Solo или Core Duo; или поддерживает SSE4 и т.д.

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

Кроме того, некоторые функции языка позволяют компилятору на С# и Java делать предположения о вашем коде, что позволяет ему оптимизировать некоторые части, которые просто небезопасны для компилятора C/С++. Когда у вас есть доступ к указателям, есть много оптимизаций, которые просто не безопасны.

Кроме того, Java и С# могут выполнять выделение кучи более эффективно, чем С++, потому что слой абстракции между сборщиком мусора и вашим кодом позволяет ему одновременно выполнять все сжатие кучи (довольно дорогостоящая операция).

Теперь я не могу говорить для Java в этой следующей точке, но я знаю, что С#, например, фактически удалит методы и вызовы методов, когда он знает, что тело метода пуст. И он будет использовать эту логику в вашем коде.

Итак, как вы можете видеть, существует множество причин, по которым некоторые реализации С# или Java будут быстрее.

Теперь все это говорит о том, что на С++ могут быть сделаны конкретные оптимизации, которые сдуют все, что вы могли бы сделать с С#, особенно в области графики и в любое время, когда вы приближаетесь к аппаратным средствам. Указатели здесь творят чудеса.

Итак, в зависимости от того, что вы пишете, я бы пошел с тем или другим. Но если вы пишете то, что не зависит от оборудования (драйвер, видеоигры и т.д.), Я бы не стал беспокоиться о производительности С# (опять-таки не о Java). Все будет хорошо.

Одна сторона Java, @Swati указывает на хорошую статью:

https://www.ibm.com/developerworks/library/j-jtp09275

Ответ 2

JIT против статического компилятора

Как уже говорилось в предыдущих сообщениях, JIT может скомпилировать IL/bytecode в собственный код во время выполнения. Стоимость этого была упомянута, но не к ее заключению:

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

Конечно, С# (или Java или VB) обычно быстрее создает жизнеспособное и надежное решение, чем С++ (хотя бы потому, что С++ имеет сложную семантику, а стандартная библиотека С++, хотя и интересная и мощная, с полным объемом стандартной библиотеки от .NET или Java), как правило, разница между С++ и .NET или Java JIT не будет видна большинству пользователей, а для тех двоичных файлов, которые являются критическими, ну, вы все же можете вызывать обработку С++ с С# или Java (даже если этот родной вызов может быть довольно дорогостоящим сам по себе)...

метапрограммирование С++

Обратите внимание, что обычно вы сравниваете код времени выполнения С++ с его эквивалентом на С# или Java. Но С++ имеет одну функцию, которая может превосходить Java/С# из коробки, это метапрограммирование шаблона: обработка кода будет выполняться во время компиляции (таким образом, увеличивая время компиляции), что приводит к нулю (или почти нулю) времени исполнения.

Я пока что вижу реальный жизненный эффект (я играл только с понятиями, но к тому времени разница была секундой исполнения для JIT и ноль для С++), но это стоит упомянуть, наряду с тем фактом, что метапрограммирование шаблона не является тривиальным...

Редактировать 2011-06-10: В С++ игра с типами выполняется во время компиляции, что означает создание общего кода, который вызывает не общий код (например, общий парсер из строки в тип T, вызывая стандартный библиотечный API для типов T, которые он распознает, и делает парсер легко расширяемым его пользователем) очень легко и очень эффективно, тогда как эквивалент в Java или С# в лучшем случае болезненен для записи и всегда будет медленнее и разрешено во время выполнения, даже когда типы известны во время компиляции, что означает, что ваша единственная надежда предназначена для JIT, чтобы встроить все это.

...

Редактировать 2011-09-20: Команда Blitz ++ (Домашняя страница, Wikipedia), и, судя по всему, их целью является достижение производительности FORTRAN при научных расчетах, перемещение максимально возможного времени от выполнения во время выполнения до времени компиляции, через метапрограммирование шаблонов С++. Таким образом, " я еще так вижу реальный эффект для этой " части, которую я написал выше, очевидно, существует в реальной жизни.

Использование памяти на Cтивный С++

С++ имеет использование памяти, отличное от Java/С#, и, следовательно, имеет разные преимущества/недостатки.

Независимо от оптимизации JIT, ничто не пойдет быстро, поскольку прямой указатель доступа к памяти (пусть игнорировать на мгновение кэширования процессора и т.д.). Таким образом, если у вас есть непрерывные данные в памяти, доступ к ним через указатели С++ (т.е. Указатели C... Пусть дает Caesar), то будет быстрее, чем в Java/С#. И С++ имеет RAII, что значительно облегчает обработку, чем на С# или даже на Java. С++ не нуждается в using для раскрытия существования его объектов. И С++ не имеет предложения finally. Это не ошибка.

: -)

И, несмотря на примитивные структуры С#, объекты С++ "на стеке" ничего не будут стоить при распределении и уничтожении, и для выполнения очистки не потребуется никакого GC для работы в независимом потоке.

Что касается фрагментации памяти, то в 2008 году распределители памяти не являются старыми распределителями памяти с 1980 года, которые обычно сравниваются с GC: распределение С++ не может быть перемещено в памяти, правда, но затем, как и в файловой системе Linux: кто требуется дефрагментация жесткого диска, когда фрагментация не происходит? Использование правильного распределителя для правильной задачи должно быть частью набора инструментов разработчика С++. Теперь писать распределители нелегко, и тогда у большинства из нас есть лучшие дела, и для большей части использования RAII или GC более чем достаточно.

Редактировать 2011-10-04: Примеры эффективных распределителей: на платформах Windows, начиная с Vista, Низкая куча фрагментациипо умолчанию. Для предыдущих версий LFH можно активировать, вызвав функцию WinAPI HeapSetInformation). В других операционных системах предоставляются альтернативные распределители (см. https://secure.wikimedia.org/wikipedia/en/wiki/Malloc для списка)

Теперь модель памяти несколько усложняется с развитием многоядерной и многопоточной технологии. В этой области, я думаю, у .NET есть преимущество, и Java, как мне сказали, держал верхнюю часть. Некоторым "хаотичным" хакерам легко хвалят его код "рядом с машиной". Но сейчас гораздо сложнее собрать лучшую сборку вручную, чем позволить компилятору выполнять свою работу. Для С++ компилятор стал обычно лучше, чем хакер с десятилетия. Для С# и Java это еще проще.

Тем не менее, новый стандарт С++ 0x накладывает простую модель памяти на компиляторы С++, которая будет стандартизировать (и, следовательно, упростить) эффективный многопроцессорный/параллельный/потоковый код на С++ и сделать оптимизацию проще и безопаснее для компиляторов. Но тогда мы увидим через несколько лет, если его promises будут считаться истинными.

С++/CLI vs. С#/VB.NET

Примечание. В этом разделе я говорю о С++/CLI, то есть С++, размещенном .NET, а не на родном С++.

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

Тот же самый код, скомпилированный в С++/CLI (или его предком, Managed С++), может быть быстрее, чем тот же код, созданный на С# (или VB.NET, компилятор которого производит тот же IL, что и С#).

Поскольку статический компилятор С++ был намного лучше для создания уже оптимизированного кода, чем С#.

Например, функция inline в .NET ограничена функциями, байт-код которых меньше или равен 32 байтам. Таким образом, некоторый код в С# будет содержать 40-байтовый аксессуар, который никогда не будет привязан JIT. Тот же код в С++/CLI создаст 20-байтовый аксессуар, который будет встроен в JIT.

Другим примером являются временные переменные, которые просто компилируются компилятором С++, но все еще упоминаются в IL, создаваемом компилятором С#. Оптимизация статической компиляции С++ приведет к уменьшению количества кода, тем самым снова разрешив более агрессивную оптимизацию JIT.

Причиной этого было предположение, что компилятор С++/CLI получил прибыль от обширных методов оптимизации из С++-компилятора.

Заключение

Я люблю С++.

Но, насколько я понимаю, С# или Java - все лучше. Не потому, что они быстрее, чем С++, а потому, что, когда вы добавляете свои качества, они становятся более продуктивными, нуждаются в меньшем количестве тренировок и имеют более полные стандартные библиотеки, чем С++. А что касается большинства программ, то их разницы в скорости (так или иначе) будут незначительными...

Изменить (2011-06-06)

Мой опыт работы на С#/. NET

У меня есть 5 месяцев почти эксклюзивного профессионального С# -кодирования (который добавляет до моего CV, уже заполненного С++ и Java, а также сенсорного С++/CLI).

Я играл с WinForms (Ahem...) и WCF (классный!) и WPF (Cool!!!! И через XAML, и с сырым С#. WPF так легко, я считаю, что Swing просто не может сравниться с ним), и С# 4.0.

Вывод состоит в том, что, хотя проще и быстрее создавать код, который работает на С#/Java, чем на С++, гораздо сложнее создать надежный и надежный код на С# (и даже сложнее на Java), чем в С++. Причины изобилуют, но его можно суммировать по:

  • Дженерики не так сильны, как шаблоны (пытаются написать эффективный общий метод Parse (от строки до T) или эффективный эквивалент boost:: lexical_cast в С# для понимания проблема)
  • RAII остается непревзойденным (GC все еще может протекать (да, я должен был справиться с этой проблемой) и будет обрабатывать только память. Даже С# using не так прост и силен, потому что запись правильная реализация Dispose сложна)
  • С# readonly и Java final нигде не так полезны, как С++ const (Там вы не можете выставлять только готовые данные (например, дерево узлов) в С# без огромной работы, в то время как это встроенная функция С++. Неизбежные данные - интересное решение, но не все может быть сделано неизменным, поэтому его даже недостаточно,).

Таким образом, С# остается приятным языком, если вы хотите что-то, что работает, но разочаровывающим языком в тот момент, когда вы хотите что-то, что всегда и безопасно работает.

Java еще более расстраивает, так как имеет те же проблемы, что и С# и т.д.: Недостаток эквивалентного ключевого слова С# using, очень опытный коллега из моих рук потратил слишком много времени, чтобы убедиться, что его ресурсы правильно освобождены, тогда как эквивалент в С++ был бы легким (с использованием деструкторов и интеллектуальных указателей).

Итак, я думаю, что повышение производительности С#/Java видится для большинства кодов... до того дня, когда вам нужно, чтобы код был как можно более совершенным. В тот день вы узнаете боль. (вы не поверите, что попросили наши серверные приложения и графические приложения...).

О стороне сервера Java и С++

Я поддерживал контакты с командами сервера (я работал 2 года среди них, прежде чем возвращаться к команде GUI), на другой стороне здания, и я узнал что-то интересное.

В последние годы тенденция заключалась в том, чтобы Java-серверные приложения были предназначены для замены старых приложений на сервере С++, поскольку Java имеет множество фреймворков/инструментов и легко поддерживается, развертывается и т.д. и т.д.

... До тех пор, пока проблема низкой латентности не восстановила свою уродливую голову в последние месяцы. Затем приложения Java-сервера, независимо от того, как оптимизировала наша опытная команда Java, просто и полностью проиграли гонку против старого, не оптимизированного сервера С++.

В настоящее время решение заключается в том, чтобы поддерживать Java-серверы для общего использования, когда производительность по-прежнему важна, не касается целевой с низкой задержкой и агрессивно оптимизирует уже более быстрые серверные приложения на С++ для приложений с низкой задержкой и ультранизким временем ожидания, латентности.

Заключение

Ничего не так просто, как ожидалось.

Java и еще больше С# - классные языки с обширными стандартными библиотеками и фреймворками, где вы можете быстро скопировать код и получить результат очень скоро.

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

Как будто вам нужно меньше времени и менее опытных разработчиков на С#/Java, чем на С++ для получения среднего качественного кода, но, с другой стороны, в тот момент, когда вам нужно было отличное качество кода, было неожиданно проще и быстрее получить результаты прямо в С++.

Конечно, это мое собственное восприятие, возможно, ограниченное нашими конкретными потребностями.

Но все же это то, что происходит сегодня, как в командах GUI, так и в командах на стороне сервера.

Конечно, я обновлю это сообщение, если произойдет что-то новое.

Изменить (2011-06-22)

"Мы находим, что в отношении производительности С++ выигрывает большой запас. Однако он также потребовал усилия по настройке, многие из которых были выполнены на уровне сложности который не будет доступен среднему программисту.

[...] Версия Java, вероятно, была самой простой в реализации, но сложнее анализировать ее производительность. В частности, эффекты, связанные с сборкой мусора, были сложными и очень трудными для настройки".

Источники:

Изменить (2011-09-20)

"Следующее слово в Facebook заключается в том, что" разумно написанный код на С++ быстро запускается, ", что подчеркивает огромные усилия, затраченные на оптимизацию кода PHP и Java. Парадоксально, что код на С++ сложнее писать чем на других языках, но эффективный код намного проще [писать на С++, чем на других языках]."

- Herb Sutter в //build/, цитируя Андрей Александреску

Источники:

Ответ 3

Всякий раз, когда я говорю об управляемой и неуправляемой производительности, я хотел бы указать, что серия Rico (и Raymond) сравнивала версии С++ и С# китайского/английского словаря. Этот google search позволит вам читать для себя, но мне нравится резюме Rico.

Так мне стыдно от моего сокрушительного поражения? Едва. Управляемый код получил очень хороший результат для почти никаких усилий. к победить управляемый Раймонд должен был:

  • Напишите свой собственный файл ввода/вывода
  • Напишите свой собственный класс строк
  • Напишите свой собственный распределитель
  • Напишите свое собственное международное сопоставление

Конечно, он использовал доступные ниже чтобы это сделать, но это еще много работы. Ты можешь позвонить что осталось от программы STL? Я не Думаю, я думаю, что он держал std::vector, который в конечном счете был никогда не было проблемой, и он продолжал находить функция. Практически все остальное не прошло.

Итак, да, вы можете победить CLR. Раймонд может сделать свою программу даже быстрее, я думаю.

Интересно, что время для разбора файл, сообщаемый обеими программами внутренние таймеры примерно одинаковы - 30 мс для каждого. Разница в накладные расходы.

Для меня суть в том, что для неуправляемой версии потребовалось 6 ревизий, чтобы победить управляемую версию, которая была простым портом исходного неуправляемого кода. Если вам нужен каждый последний бит производительности (и у вас есть время и знания, чтобы получить его), вам придется идти неуправляемым, но для меня я возьму на себя порядок величины, который у меня есть в первых версиях по сравнению с 33 % Я получаю, если я попробую 6 раз.

Ответ 4

Компиляция для конкретных оптимизаций ЦП обычно переоценивается. Просто возьмите программу на С++ и скомпилируйте с оптимизацией для pentium PRO и запустите pentium 4. Затем перекомпилируйте с оптимизацией для pentium 4. Я прошел долгие дни, выполняя это с несколькими программами. Общие результаты? Обычно производительность не превышает 2-3%. Таким образом, теоретические преимущества JIT почти отсутствуют. Большинство различий производительности можно наблюдать только при использовании функций скалярной обработки данных, что в конечном итоге потребует ручной тонкой настройки для достижения максимальной производительности в любом случае. Оптимизации такого рода выполняются медленно и дорого, что делает их иногда непригодными для JIT в любом случае.

В реальном мире и реальном приложении С++ по-прежнему обычно быстрее, чем java, главным образом из-за более легкой памяти, что приводит к повышению производительности кэша.

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

Ответ 5

JIT (Just In Time Compiling) может быть невероятно быстрым, потому что он оптимизирован для целевой платформы.

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

Основная концепция .NET JIT работает так (сильно упрощена):

Вызов метода в первый раз:

  • Ваш программный код вызывает метод Foo()
  • CLR просматривает тип, реализующий Foo(), и получает связанные с ним метаданные
  • Из метаданных CLR знает, какой адрес памяти хранится в IL (промежуточный байт-код).
  • CLR выделяет блок памяти и вызывает JIT.
  • JIT компилирует IL в собственный код, помещает его в выделенную память и затем меняет указатель на функцию в метаданных типа Foo(), чтобы указать на этот собственный код.
  • Начинается собственный код.

Вызов метода во второй раз:

  • Ваш программный код вызывает метод Foo()
  • CLR просматривает тип, реализующий Foo(), и находит указатель функции в метаданных.
  • Выполняется запуск собственного кода в этой ячейке памяти.

Как вы можете видеть, во второй раз это практически тот же процесс, что и С++, за исключением преимуществ оптимизации в реальном времени.

Тем не менее, есть еще другие служебные проблемы, которые замедляют управляемый язык, но JIT помогает много.

Ответ 6

Мне нравится Orion Adrian, но есть еще один аспект.

Тот же вопрос был поставлен несколько десятилетий назад на языке ассемблера против "человеческих" языков, таких как FORTRAN. И часть ответа схожа.

Да, программа на С++ может быть быстрее, чем С# для любого заданного (нетривиального?) алгоритма, но программа на С# часто будет такой же быстрой или быстрой, чем "наивная" реализация на С++, а оптимизированная версия на С++ займет больше времени и может по-прежнему превзойти версию С# с небольшим отрывом. Итак, действительно ли это стоит?

Вам придется ответить на этот вопрос по одному.

Тем не менее, я долгое время болею за С++, и я думаю, что это невероятно выразительный и мощный язык - иногда недооцениваемый. Но во многих проблемах "реальной жизни" (для меня лично это означает "тот, который мне платят, чтобы решить" ), С# быстрее выполнит работу и станет более безопасным.

Самый большой штраф, который вы платите? Многие программы .NET и Java - это хранилища памяти. Я видел, как .NET и Java-приложения занимают "сотни" мегабайт памяти, когда программы на C++ аналогичной сложности едва поцарапают "десятки" MB.

Ответ 7

Я не уверен, как часто вы обнаружите, что Java-код будет работать быстрее, чем С++, даже с Hotspot, но я расскажу, как это может произойти.

Подумайте о компилированном Java-коде как интерпретируемый машинный язык для JVM. Когда процессор Hotspot замечает, что некоторые фрагменты скомпилированного кода будут использоваться много раз, он выполняет оптимизацию на машинный код. Поскольку сборка вручную почти всегда быстрее, чем скомпилированный код на С++, вполне понятно, что программно-настроенный машинный код не будет слишком плохим.

Итак, для сильно повторяющегося кода я мог видеть, где можно было бы запустить Hotspot JVM для запуска Java быстрее, чем С++... пока сбор мусора не начнется.:)

Ответ 8

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

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

Кроме того, С++ догоняет "новые" (обратите внимание на кавычки) такие функции, как контейнеры STL, автонауки и т.д. - см., например, библиотеку boost. И вы иногда можете обнаружить, что самый быстрый способ выполнить какую-то задачу требует такой техники, как арифметика указателей, которая запрещена на языке более высокого уровня, хотя они, как правило, позволяют вам обращаться в библиотеку, написанную на языке, который может реализовать его по желанию.

Главное знать язык, который вы используете, связанный с ним API, что он может делать и каковы его ограничения.

Ответ 9

Я тоже не знаю... мои Java-программы всегда медленны.:-) Я никогда не замечал, что программы на С# особенно медленны.

Ответ 10

Вот еще один примерный тест, который вы можете попробовать самостоятельно на своем компьютере.

Он сравнивает ASM, VС++, С#, Silverlight, Java-апплет, Javascript, Flash (AS3)

Демо-версия плагина Roozz

Обратите внимание, что скорость javascript много видоизменяется в зависимости от того, какой браузер ее выполняет. То же самое верно для Flash и Silverlight, потому что эти плагины работают в том же процессе, что и хостинг-браузер. Но плагин Roozz запускает стандартные .exe файлы, которые запускаются в их собственном процессе, поэтому на скорость не влияет хостинг-браузер.

Ответ 11

Вы должны определить "выполнить лучше, чем...". Ну, я знаю, вы спросили о скорости, но это не все, что имеет значение.

  • У виртуальных машин больше времени на выполнение? Да!
  • Есть ли у них больше рабочей памяти? Да!
  • Есть ли у них более высокие затраты на запуск (инициализация времени выполнения и компилятор JIT)? Да!
  • Требуется ли им установка огромной библиотеки? Да!

И так далее, его предвзятое, да;)

С С# и Java вы платите цену за то, что получаете (более быстрое кодирование, автоматическое управление памятью, большая библиотека и т.д.). Но у вас мало места для обсуждения деталей: возьмите полный пакет или ничего.

Даже если эти языки могут оптимизировать некоторый код для выполнения быстрее, чем скомпилированный код, весь подход (IMHO) неэффективен. Представьте, что вы едете каждый день в 5 милях на свое рабочее место, с грузовиком! Его удобно, он чувствует себя хорошо, вы в безопасности (экстремальная зона смятия), и после того, как вы на некоторое время поработаете над газом, это будет даже так же быстро, как стандартный автомобиль! Почему у всех нас нет грузовика, чтобы ездить на работу?;)

В С++ вы получаете то, за что платите, не больше, не меньше.

Цитата Bjarne Stroustrup: "С++ - мой любимый сборщик мусора, потому что он генерирует так мало мусора" текст ссылки

Ответ 12

Исполняемый код, созданный компилятором Java или С#, не интерпретируется - он компилируется в собственный код "как раз вовремя" (JIT). Итак, первый временной код в программе Java/С# встречается во время выполнения, есть некоторые накладные расходы, поскольку "компилятор времени выполнения" (как компилятор JIT) превращает байтовый код (Java) или код IL (С#) в нативные машинные инструкции. Однако в следующий раз, когда этот код встречается во время работы приложения, собственный код выполняется немедленно. Это объясняет, как некоторые программы Java/С# кажутся медленными изначально, но затем лучше работают, чем дольше они работают. Хорошим примером является веб-сайт ASP.Net. В первый раз, когда веб-сайт доступен, он может быть немного медленнее, поскольку код С# скомпилирован в собственный код компилятором JIT. Последующие обращения приводят к значительно более быстрому веб-сайту - от кеширования сервера и клиентской стороны.

Ответ 13

Языки виртуальной машины вряд ли будут превосходить скомпилированные языки, но они могут приблизиться настолько, что это не имеет значения, поскольку (по крайней мере) следующие причины (я говорю для Java здесь, так как я никогда не делал С#).

1/Java Runtime Environment обычно может обнаруживать фрагменты кода, которые выполняются часто, и выполнять компиляцию этих разделов точно в срок (JIT), чтобы в будущем они выполнялись с полной скомпилированной скоростью.

2/Огромные части библиотек Java скомпилированы таким образом, что при вызове функции библиотеки вы выполняете скомпилированный код, а не интерпретируете. Вы можете увидеть код (на C), загрузив OpenJDK.

3/Если вы не выполняете массовые вычисления, большую часть времени ваша программа работает, она ждет ввода от очень медленного (относительно говорящего) человека.

4/Поскольку большая часть проверки байт-кода Java выполняется во время загрузки класса, нормальные накладные расходы на проверки выполнения значительно сокращаются.

5/В худшем случае высокопроизводительный код можно извлечь в скомпилированный модуль и вызвать из Java (см. JNI), чтобы он работал на полной скорости.

Таким образом, байт-код Java никогда не будет превосходить собственный машинный язык, но есть способы смягчить это. Большим преимуществом Java (как я вижу) является HUGE стандартная библиотека и кросс-платформенный характер.

Ответ 14

Некоторые хорошие ответы здесь о конкретном вопросе, который вы задали. Я хотел бы отступить и посмотреть на большую картину.

Имейте в виду, что ваше восприятие пользователем скорости написанного вами программного обеспечения зависит от многих других факторов, чем от того, насколько хорошо оптимизирован код. Вот несколько примеров:

  • Ручное управление памятью сложно сделать правильно (без утечек), и еще сложнее сделать эффективно (свободная память вскоре после того, как вы закончите с ней). Использование GC, как правило, с большей вероятностью создает программу, которая хорошо управляет памятью. Готовы ли вы работать очень тяжело и откладывать доставку своего программного обеспечения в попытке выйти из GC?

  • Мой С# легче читать и понимать, чем мой С++. У меня также есть больше способов убедить себя, что мой С#-код работает правильно. Это означает, что я могу оптимизировать свои алгоритмы с меньшим риском появления ошибок (и пользователям не нравится программное обеспечение, которое выходит из строя, даже если оно выполняется быстро!)

  • Я могу создать свое программное обеспечение быстрее на С#, чем на С++. Это освобождает время для работы над производительностью и по-прежнему доставляет мое программное обеспечение вовремя.

  • Легче писать хороший пользовательский интерфейс в С#, чем С++, поэтому я с большей вероятностью смогу работать на фоне, в то время как пользовательский интерфейс остается отзывчивым или обеспечивает прогресс или пользовательский интерфейс listenbeat, когда программа должна блокировать какое-то время. Это не делает ничего быстрее, но это делает пользователей счастливее ждать.

Все, что я сказал о С#, вероятно, верно для Java, у меня просто нет уверенности сказать.

Ответ 15

Если вы программист на Java/С#, изучающий С++, у вас возникнет соблазн продолжать думать в терминах Java/С# и переводить дословно на синтаксис С++. В этом случае вы получаете только ранее упомянутые преимущества собственного кода или интерпретируемого /JIT. Чтобы получить наибольшее увеличение производительности на С++ или Java/С#, вам нужно научиться думать на С++ и разрабатывать код специально для использования сильных сторон С++.

Перефразировать Edsger Dijkstra: [ваш первый язык] искажает ум без восстановления.
Перефразировать Jeff Atwood: вы можете написать [свой первый язык] на любом новом языке.

Ответ 16

Одной из наиболее значительных оптимизаций JIT является метод встраивания. Java может даже встроить виртуальные методы, если это может гарантировать правильность выполнения. Такой вид оптимизации обычно не может выполняться стандартными статическими компиляторами, поскольку для этого требуется анализ всей программы, что сложно из-за отдельной компиляции (напротив, у JIT есть все доступные ей программы). Метод inlining улучшает другие оптимизации, предоставляя большие блоки кода для оптимизации.

Стандартное распределение памяти в Java/С# также выполняется быстрее, а освобождение (GC) не намного медленнее, но только менее детерминировано.

Ответ 17

Орион Адриан, позвольте мне изменить ваше сообщение, чтобы увидеть, насколько необоснованны ваши замечания, потому что многое можно сказать и о С++. И говоря, что компилятор Java/С# оптимизирует пустые функции, действительно заставляет вас звучать так, будто вы не являетесь моим экспертом в области оптимизации, потому что: а) почему настоящая программа должна содержать пустые функции, за исключением действительно плохого старого кода, b) это действительно не черная и кратковременная оптимизация.

Помимо этой фразы, вы откровенно пишете о указателях, но не объекты в Java и С# в основном работают как указатели С++? Может ли они не пересекаться? Могут ли они быть недействительными? C (и большинство реализаций на С++) имеет ключевое слово ограничения, оба имеют типы значений, С++ имеет ссылку на значение с ненулевой гарантией. Что предлагают Java и С#?

→ → → → →

Как правило, C и С++ могут быть такими же быстрыми или быстрыми, потому что компилятор AOT - компилятор, который компилирует ваш код перед развертыванием, раз и навсегда, на вашей высокой памяти, на многих серверах ядра, может сделать оптимизацию, чтобы С# скомпилированная программа не может, потому что у нее есть много времени для этого. Компилятор может определить, является ли машина Intel или AMD; Pentium 4, Core Solo или Core Duo; или поддерживает SSE4 и т.д., и если ваш компилятор не поддерживает диспетчеризацию во время выполнения, вы можете решить это самостоятельно, развернув несколько специализированных двоичных файлов.

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

Кроме того, некоторые функции языка позволяют компилятору на С++ или C делать предположения о вашем коде, что позволяет ему оптимизировать некоторые части, которые просто небезопасны для компилятора Java/С#. Когда у вас нет доступа к полному идентификатору типа дженериков или гарантированному потоку программы, существует множество оптимизаций, которые просто не безопасны.

Кроме того, С++ и C выполняют множество распределений стека одновременно с помощью только одной инкремента регистров, что, безусловно, более эффективно, чем распределения Javas и С#, как для уровня абстракции между сборщиком мусора и вашим кодом.

Теперь я не могу говорить для Java в этой следующей точке, но я знаю, что компиляторы С++, например, будут фактически удалять методы и вызовы методов, когда он знает, что тело метода пуст, оно устранит общие подвыражения, оно может попробуйте и повторите попытку, чтобы найти оптимальное использование регистров, оно не обеспечивает проверку границ, оно будет автоматически определять петли и внутренние циклы и инвертировать внутреннее к внешнему, оно перемещает условия из циклов, разбивает и раскладывает петли. Он расширит std::vector на собственные нулевые служебные массивы, как вы делаете C-путь. Он будет выполнять межпроцедурные оптимизации. Он будет строить возвращаемые значения непосредственно на сайте вызывающего абонента. Он будет складывать и распространять выражения. Он будет переупорядочивать данные в режиме кэширования. Он будет выполнять прыжки по резьбе. Он позволяет писать компиляторы временных лучей с нулевыми накладными расходами. Это сделает очень дорогостоящие оптимизации на основе графиков. Это приведет к сокращению силы, заменит ли он некоторые коды синтаксически совершенно неравным, но семантически эквивалентным кодом (старый "xor foo, foo" - это просто самая простая, хотя и устаревшая оптимизация такого рода). Если вы любезно спросите об этом, вы можете опустить стандарты с плавающей точкой IEEE и включить еще больше оптимизаций, таких как переопределение операндов с плавающей запятой. После того, как он массирует и уничтожает ваш код, он может повторить весь процесс, потому что часто определенные оптимизации закладывают основу даже для определенных оптимизаций. Он также может просто повторить с перетасованными параметрами и посмотреть, как другой вариант оценивается во внутреннем рейтинге. И он будет использовать эту логику в вашем коде.

Итак, как вы можете видеть, существует множество причин, по которым некоторые реализации С++ или C будут быстрее.

Теперь все это говорит о том, что на С++ можно сделать много оптимизаций, которые сдуют все, что вы можете сделать с С#, особенно в области хруста, числа в реальном времени и близко к металлу, но не только там. Вам даже не нужно прикасаться к одному указателю, чтобы пройти долгий путь.

Итак, в зависимости от того, что вы пишете, я бы пошел с тем или другим. Но если вы пишете то, что не зависит от оборудования (драйвер, видеоигры и т.д.), Я бы не стал беспокоиться о производительности С# (опять-таки не о Java). Все будет хорошо.

< < < < < < < < < <

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

В любом случае, чтобы мир: AOT велик, как и JIT. Единственный правильный ответ может быть: это зависит. И настоящие умные люди знают, что вы все равно можете использовать лучшее из обоих миров.

Ответ 18

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

Однако шансы на то, что на самом деле происходит, довольно низки - если, возможно, Java не имеет очень хорошо написанной библиотеки, и у вас есть ваша собственная плохо написанная библиотека С++.

Ответ 19

Собственно, С# на самом деле не работает на виртуальной машине, как это делает Java. IL скомпилирован в язык ассемблера, который является полностью родным кодом и работает с той же скоростью, что и собственный код. Вы можете использовать JIT.NET-приложение, которое полностью удаляет стоимость JIT, а затем вы используете полностью собственный код.

Замедление с .NET произойдет не потому, что .NET-код работает медленнее, а потому, что он делает гораздо больше за кулисами, чтобы делать что-то вроде сбора мусора, проверять ссылки, хранить полные кадры стека и т.д. Это может быть довольно мощным и полезно при создании приложений, но также стоит дорого. Обратите внимание, что вы можете делать все это в программе на С++ (большая часть основной функциональности .NET - это действительно код .NET, который вы можете просматривать в ROTOR). Однако, если вы написали одну и ту же функциональность, вы, вероятно, закончите с гораздо более медленной программой, так как среда выполнения .NET была оптимизирована и точно настроена.

Тем не менее, одна из сильных сторон управляемого кода заключается в том, что он может быть полностью поддающимся проверке, т.е. вы можете убедиться, что код никогда не будет обращаться к другой памяти процессов или выполнять нечеткие вещи перед их выполнением. У Microsoft есть прототип исследования полностью управляемой операционной системы, которая удивительно показала, что 100% управляемая среда может фактически выполнять значительно быстрее, чем любая современная операционная система, используя эту проверку, чтобы отключить функции безопасности, которые больше не нужны управляемым программам (в некоторых случаях мы говорим, как 10 раз). В радиостанции SE есть большой эпизод, рассказывающий об этом проекте.

Ответ 20

В некоторых случаях управляемый код может быть быстрее, чем собственный. Например, алгоритмы сбора мусора "mark-and-sweep" позволяют средам, таким как JRE или CLR, освобождать большое количество короткоживущих (обычно) объектов за один проход, где большинство объектов кучи C/С++ освобождаются одним-на- а-время.

От wikipedia:

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

Тем не менее, я написал много С# и много С++, и я провел много тестов. По моему опыту, С++ намного быстрее, чем С#, двумя способами: (1) если вы берете код, написанный на С#, переносите его на С++, то собственный код имеет тенденцию быть быстрее. Насколько быстрее? Ну, это варьируется очень много, но не редкость видеть 100% -ное улучшение скорости. (2) В некоторых случаях сбор мусора может значительно замедлить управляемое приложение..NET CLR делает ужасную работу с большими кучами (скажем, > 2 ГБ) и может потратить много времени в GC - даже в приложениях, которые имеют мало или даже нет объектов промежуточного периода жизни.

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

Ответ 22

Фактически Sun HotSpot JVM использует "смешанный режим". Он интерпретирует метод bytecode до тех пор, пока он не определит (обычно через счетчик какого-либо рода), что конкретный блок кода (метод, цикл, блок try-catch и т.д.) Будет выполняться много, а затем JIT скомпилирует его. Время, требуемое для компиляции метода JIT, часто занимает больше времени, чем если бы метод был интерпретирован, если он является методом с низкой вероятностью. Производительность обычно выше для "смешанного режима", потому что JVM не тратит время на JIT-код, который редко выполняется, если вообще когда-либо выполняется. С# и .NET этого не делают..NET JIT все, что часто случается, тратит время.

Ответ 24

Go прочитайте о HP Labs Dynamo, интерпретаторе PA-8000, который работает на PA-8000, и часто запускает программы быстрее, чем изначально. Тогда это ничуть не удивительно!

Не думайте об этом как о "промежуточном шаге" - запуск программы включает в себя множество других шагов уже на любом языке.

Это часто сводится к:

  • у программ есть горячие точки, поэтому, даже если вы медленнее выполняете 95% кода, который вы должны запускать, вы все равно можете быть конкурентоспособным по производительности, если вы быстрее на 5%/p >

  • HLL знает больше о ваших намерениях, чем LLL, например C/С++, и поэтому может генерировать более оптимизированный код (OCaml имеет еще больше и на практике часто даже быстрее)

  • JIT-компилятор имеет много информации, которую статический компилятор не делает (например, фактические данные, которые у вас есть на этот раз)

  • JIT-компилятор может выполнять оптимизацию во время выполнения, которое традиционным линкерам на самом деле не разрешено (например, переупорядочивание ветвей, чтобы общий случай был плоским или встраивал вызовы библиотеки)

В целом, C/С++ - довольно отвратительные языки для производительности: относительно небольшая информация о ваших типах данных, отсутствие информации о ваших данных и отсутствие динамической среды выполнения, позволяющей значительно оптимизировать время выполнения.

Ответ 25

Вы можете получить короткие всплески, когда Java или CLR быстрее, чем С++, но в целом производительность хуже для жизни приложения: см. www.codeproject.com/KB/dotnet/RuntimePerformance.aspx для некоторых результатов для этого.

Ответ 26

Я понимаю, что C/С++ создает собственный код для работы на определенной машинной архитектуре. И наоборот, такие языки, как Java и С#, запускаются поверх виртуальной машины, которая абстрагирует оттуда собственную архитектуру. Логически для Java или С# было бы невозможно сопоставить скорость С++ из-за этого промежуточного шага, однако мне сказали, что последние компиляторы ( "горячая точка" ) могут достичь этой скорости или даже превысить ее.

Это нелогично. Использование промежуточного представления не ухудшает производительность. Например, llvm-gcc компилирует C и С++ через LLVM IR (который является виртуальной машиной с бесконечным регистром) в собственный код и обеспечивает отличную производительность (часто избиение GCC).

Возможно, это скорее вопрос компилятора, чем вопрос на языке, но может ли кто-нибудь объяснить на простом английском языке, как один из этих языков виртуальных машин может работать лучше, чем родной?

Вот несколько примеров:

  • Виртуальные машины с компиляцией JIT облегчают создание кода во время выполнения (например, System.Reflection.Emit на .NET), поэтому вы можете скомпилировать сгенерированный код на лету на таких языках, как С# и F #, но прибегать к написанию сравнительно -строчный интерпретатор на C или С++. Например, для реализации регулярных выражений.

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

  • Динамическое связывание собственного кода требует соответствия ABI, которое может препятствовать производительности и устраняет оптимизацию всей программы, тогда как связь обычно откладывается на виртуальных машинах и может извлечь выгоду из оптимизации всей программы (например, генерируемых генераторами .NET).

Я также хотел бы остановиться на некоторых проблемах с высокоприоритетным ответом paercebal выше (потому что кто-то продолжает удалять мои комментарии по его ответу), который представляет противоположно продуктивно поляризованный вид:

Обработка кода будет выполняться во время компиляции...

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

... игра с типами выполняется во время компиляции... эквивалент в Java или С# в лучшем случае болезненен для записи и всегда будет медленнее и разрешен во время выполнения, даже когда типы известны во время компиляции.

В С# это относится только к ссылочным типам и не относится к типам значений.

Независимо от оптимизации JIT, ничто не пойдет быстро, поскольку прямой указатель доступа к памяти... если у вас есть непрерывные данные в памяти, доступ к ним через указатели С++ (то есть C-указатели... пусть дает Caesar) быстрее, чем в Java/С#.

Люди наблюдали Java, избивающий С++ в тесте SOR из теста SciMark2 именно потому, что указатели препятствуют оптимизации, связанной с псевдонимом.

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

Ответ 27

В дополнение к тому, что говорили некоторые другие, из моего понимания .NET и Java лучше распределяются по памяти. Например. они могут компактно хранить память, поскольку она фрагментируется, а С++ не может (изначально, но может быть, если вы используете умный сборщик мусора).

Ответ 28

Для чего-то, нуждающегося в большой скорости, JVM просто вызывает реализацию С++, поэтому вопрос о том, насколько хороши их библиотеки, - это то, насколько хорошо JVM для большинства связанных с ОС вещей. Сбор мусора сокращает вашу память пополам, но использование некоторых функций STL и Boost для любителей будет иметь такой же эффект, но с многократным увеличением вероятности ошибок.

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

Однако преимущество С++ в том, что оно позволяет вам оптимизировать себя, иначе вы застряли в том, что делает компилятор /jvm. Если вы создадите свои собственные контейнеры, напишите собственное управление памятью, которое выровнено, используйте SIMD и перейдите к сборке здесь и там, вы можете ускорить, по крайней мере, в 2x-4 раза больше того, что большинство компиляторов С++ будут делать самостоятельно. Для некоторых операций 16x-32x. При использовании тех же алгоритмов, если вы используете лучшие алгоритмы и распараллеливаете, увеличение может быть драматичным, иногда в тысячи раз быстрее, чем обычно используемые методы.

Ответ 29

Я смотрю на это из нескольких разных точек.

  • Учитывая бесконечное время и ресурсы, управляемый или неуправляемый код будет быстрее? Ясно, что ответ заключается в том, что неуправляемый код всегда может по крайней мере связывать управляемый код в этом аспекте - как в худшем случае, вы просто жестко кодируете решение управляемого кода.
  • Если вы берете программу на одном языке и напрямую переводите ее на другую, насколько она будет хуже? Наверное, много, для любых двух языков. Большинство языков требуют разных оптимизаций и имеют разные getchas. Микро-производительность часто связана с пониманием этих деталей.
  • Учитывая конечное время и ресурсы, какой из двух языков даст лучший результат? Это самый интересный вопрос, так как в то время как управляемый язык может создавать несколько более медленный код (учитывая программу, написанную для этого языка), эта версия, скорее всего, будет выполнена раньше, что позволит увеличить время на оптимизацию.

Ответ 30

Очень короткий ответ: учитывая фиксированный бюджет, вы достигнете более эффективного применения Java-приложения, чем приложение С++ (соображения ROI). Кроме того, на платформе Java есть более приличные профилировщики, которые помогут вам быстрее определить ваши горячие точки.