Насколько быстрее C ++, чем C #? - PullRequest
       112

Насколько быстрее C ++, чем C #?

219 голосов
/ 26 сентября 2008

Или сейчас наоборот?

Из того, что я слышал, есть некоторые области, в которых C # оказывается быстрее, чем C ++, но у меня никогда не было смелости проверить это самостоятельно.

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

Ответы [ 27 ]

308 голосов
/ 26 сентября 2008

Нет строгой причины, по которой язык на основе байт-кода, такой как C # или Java, который имеет JIT, не может быть таким же быстрым, как код C ++. Однако код C ++ долгое время был значительно быстрее, а также сегодня во многих случаях. Это происходит главным образом из-за того, что более сложные JIT-оптимизации сложны для реализации, а по-настоящему крутые из них появляются только сейчас.

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

С другой стороны, код на интерпретируемых языках становится быстрее в более поздних версиях среды выполнения (.NET CLR или Java VM) без каких-либо действий. И есть много полезных оптимизаций, которые JIT-компиляторы могут сделать, что просто невозможно в языках с указателями. Кроме того, некоторые утверждают, что сборка мусора, как правило, должна быть такой же быстрой или быстрой, как ручное управление памятью, и во многих случаях это так. Как правило, все это можно реализовать и реализовать на C ++ или C, но это будет намного сложнее и подвержено ошибкам.

Как сказал Дональд Кнут, «преждевременная оптимизация - корень всего зла». Если вы действительно точно знаете, что ваше приложение будет в основном состоять из арифметики, очень критичной к производительности, и что это будет узким местом, и, безусловно, будет быстрее в C ++, и вы уверены, что C ++ не будет конфликтовать с другими требования, перейти на C ++. В любом другом случае сначала сконцентрируйтесь на правильной реализации приложения на любом языке, который вам больше подходит, затем найдите узкие места в производительности, если он работает слишком медленно, а затем подумайте о том, как оптимизировать код. В худшем случае вам может потребоваться обратиться к коду C через интерфейс сторонней функции, поэтому у вас все еще будет возможность писать критические части на языке более низкого уровня.

Имейте в виду, что оптимизировать правильную программу относительно легко, но гораздо труднее исправить оптимизированную программу.

Дать фактический процент преимуществ скорости невозможно, это во многом зависит от вашего кода. Во многих случаях реализация языка программирования даже не является узким местом. С большим скептицизмом принимайте тесты http://benchmarksgame.alioth.debian.org/, так как они во многом тестируют арифметический код, который, скорее всего, совсем не похож на ваш код.

182 голосов
/ 26 сентября 2008

C # не может быть быстрее, но это делает ВЫ / МЕНЯ быстрее. Это самая важная мера для того, что я делаю. :)

78 голосов
/ 26 сентября 2008

Это пять апельсинов быстрее. Или скорее: не может быть (правильного) общего ответа. C ++ - это статически скомпилированный язык (но есть и оптимизация с учетом профилей), C # запускается с помощью JIT-компилятора. Существует так много различий, что на такие вопросы, как «насколько быстрее» невозможно ответить, даже задавая порядки.

67 голосов
/ 08 сентября 2016

Я собираюсь начать с несогласия с частью принятого (и хорошо оцененного) ответа на этот вопрос, заявив:

На самом деле существует множество причин, по которым код JITted будет работать медленнее, чем должным образом оптимизированный C ++ (или другой язык без дополнительных затрат времени выполнения) программа в том числе:

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

  • любые горячие пути в JITter будут конкурировать с вашим кодом для инструкций и кэширования данных в CPU. Мы знаем, что кэш доминирует, когда дело доходит до производительности, и родные языки, такие как C ++, по определению не имеют такого типа конфликтов.

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

Итог: в конечном счете, вы почти наверняка сможете создать более быструю реализацию в C ++, чем в C # .

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

Это очень длинная и сложная тема, но я считаю, что ради полноты стоит упомянуть, что оптимизатор времени выполнения C # превосходен и способен выполнять определенные динамические оптимизации во время выполнения, которые просто недоступны для C ++ с его компиляцией временный (статический) оптимизатор. Даже при этом, как правило, преимущество по-прежнему глубоко в суде нативного приложения, но динамический оптимизатор является причиной приведенного выше квалификатора « почти , безусловно».

-

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

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

Вместо этого я написал немного больше идиоматического кода C ++ и сравнил его с кодом C # @Wiory. Два основных изменения, которые я внес в код C ++:

1) используемый вектор :: резерв ()

2) сглаживает массив 2d до 1d для достижения лучшей локализации кэша (непрерывный блок)

C # (.NET 4.6.1)

private static void TestArray()
{
    const int rows = 5000;
    const int columns = 9000;
    DateTime t1 = System.DateTime.Now;
    double[][] arr = new double[rows][];
    for (int i = 0; i < rows; i++)
        arr[i] = new double[columns];
    DateTime t2 = System.DateTime.Now;

    Console.WriteLine(t2 - t1);

    t1 = System.DateTime.Now;
    for (int i = 0; i < rows; i++)
        for (int j = 0; j < columns; j++)
            arr[i][j] = i;
    t2 = System.DateTime.Now;

    Console.WriteLine(t2 - t1);
}

Время выполнения (выпуск): Инициализация: 124 мс, Заполнение: 165 мс

C ++ 14 (Clang v3.8 / C2)

#include <iostream>
#include <vector>

auto TestSuite::ColMajorArray()
{
    constexpr size_t ROWS = 5000;
    constexpr size_t COLS = 9000;

    auto initStart = std::chrono::steady_clock::now();

    auto arr = std::vector<double>();
    arr.reserve(ROWS * COLS);

    auto initFinish = std::chrono::steady_clock::now();
    auto initTime = std::chrono::duration_cast<std::chrono::microseconds>(initFinish - initStart);

    auto fillStart = std::chrono::steady_clock::now();

    for(auto i = 0, r = 0; r < ROWS; ++r)
    {
        for (auto c = 0; c < COLS; ++c)
        {
            arr[i++] = static_cast<double>(r * c);
        }
    }

    auto fillFinish = std::chrono::steady_clock::now();
    auto fillTime = std::chrono::duration_cast<std::chrono::milliseconds>(fillFinish - fillStart);

    return std::make_pair(initTime, fillTime);
}

Время выполнения (выпуск): Инициализация: 398 мкс (да, это микросекунды), Заполнение: 152 мс

Общее время выполнения: C #: 289 мс, C ++ 152 мс (примерно на 90% быстрее)

Наблюдения

  • Изменение реализации C # на ту же реализацию 1d массива получено Init: 40 мс, Fill: 171 мс, итого: 211 мс ( C ++ был почти На 40% быстрее ).

  • Намного сложнее спроектировать и написать «быстрый» код на C ++, чем писать «обычный» код на любом языке.

  • (возможно) удивительно легко получить низкую производительность в C ++; мы видели это с незарезервированными векторами производительности. И таких ловушек много.

  • Производительность C # довольно удивительна, если учесть все, что происходит во время выполнения. И эта производительность сравнительно легко доступ.

  • Дополнительные случайные данные, сравнивающие производительность C ++ и C #: https://benchmarksgame.alioth.debian.org/u64q/compare.php?lang=gpp&lang2=csharpcore

Суть в том, что C ++ дает вам гораздо больший контроль над производительностью. Вы хотите использовать указатель? Ссылка? Стек памяти? Heap? Динамический полиморфизм или устранение накладных расходов во время выполнения виртуальной таблицы со статическим полиморфизмом (через шаблоны / CRTP)? В C ++ вы должны ... э-э, добраться до самостоятельно делать все эти выборы (и даже больше), в идеале, чтобы ваше решение наилучшим образом решало проблему, которую вы решаете.

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

58 голосов
/ 26 сентября 2008

По моему опыту (и я много работал с обоими языками), главная проблема с C # по сравнению с C ++ - это высокое потребление памяти, и я не нашел хорошего способа управления им. Именно потребление памяти в конечном итоге замедлит работу программного обеспечения .NET.

Другим фактором является то, что JIT-компилятор не может позволить себе слишком много времени для выполнения расширенных оптимизаций, потому что он выполняется во время выполнения, и конечный пользователь заметит это, если это займет слишком много времени. С другой стороны, компилятор C ++ имеет все время, необходимое для оптимизации во время компиляции. Этот фактор гораздо менее значим, чем потребление памяти, ИМХО.

33 голосов
/ 26 сентября 2008

Один конкретный сценарий, в котором C ++ по-прежнему имеет преимущество (и будет в ближайшие годы), возникает, когда полиморфные решения могут быть заранее определены во время компиляции.

Как правило, инкапсуляция и отложенное принятие решений - это хорошая вещь, потому что они делают код более динамичным, легче адаптируются к изменяющимся требованиям и легче используются в качестве основы. Вот почему объектно-ориентированное программирование на C # очень продуктивно и его можно обобщить под термином «обобщение». К сожалению, этот конкретный тип обобщения обходится во время выполнения.

Обычно эта стоимость несущественна, но есть приложения, в которых накладные расходы на вызовы виртуальных методов и создание объектов могут иметь значение (особенно потому, что виртуальные методы предотвращают другие оптимизации, такие как встраивание вызовов методов). Именно здесь C ++ имеет огромное преимущество, поскольку вы можете использовать шаблоны для достижения другого вида обобщения, который оказывает no влияние на время выполнения, но не обязательно является менее полиморфным, чем ООП. Фактически, все механизмы, которые составляют ООП, могут быть смоделированы с использованием только шаблонных методов и разрешения во время компиляции.

В таких случаях (и по общему признанию, они часто ограничены специальными проблемными областями), C ++ выигрывает у C # и сопоставимых языков.

19 голосов
/ 26 сентября 2008

C ++ (или C в этом отношении) дает вам детальный контроль над вашими структурами данных. Если вы хотите немного покататься, у вас есть такая опция. Большие управляемые приложения Java или .NET (OWB, Visual Studio 2005 ), которые используют внутренние структуры данных библиотек Java / .NET, несут с собой багаж. Я видел сеансы OWB-дизайнеров, использующие более 400 МБ ОЗУ и BIDS для куба или ETL , которые также попадают в сотню МБ.

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

IMO для больших приложений, разница не столько в JIT, сколько в структурах данных, которые использует сам код. Если приложение занимает много памяти, вы получите менее эффективное использование кэша. Промах кэша на современных процессорах довольно дорогой. Где C или C ++ действительно выигрывают, это то, где вы можете оптимизировать использование структур данных, чтобы хорошо играть с кэшем ЦП.

18 голосов
/ 27 сентября 2008

Для графики стандартный класс C # Graphics намного медленнее, чем GDI, доступ к которому осуществляется через C / C ++. Я знаю, что это не имеет ничего общего с языком как таковым, больше с общей платформой .NET, но графика - это то, что предлагается разработчику в качестве замены GDI, а ее производительность настолько плоха, что я даже не осмелюсь сделать графику с этим.

У нас есть простой тест, который мы используем, чтобы увидеть, насколько быстро работает графическая библиотека, и это просто рисование случайных линий в окне. C ++ / GDI по-прежнему быстры с 10000 строками, в то время как C # / Graphics с трудом делает 1000 в режиме реального времени.

13 голосов
/ 08 декабря 2008

Сборка мусора является основной причиной, по которой Java # НЕ МОЖЕТ использоваться для систем реального времени.

  1. Когда произойдет GC?

  2. Сколько времени это займет?

Это недетерминированный.

11 голосов
/ 30 декабря 2008

Мы должны были определить, был ли C # сопоставим по производительности с C ++, и я написал несколько тестовых программ для этого (используя Visual Studio 2005 для обоих языков). Оказалось, что без сборки мусора и только с учетом языка (не фреймворка) C # имеет в основном ту же производительность, что и C ++. Выделение памяти намного быстрее в C #, чем в C ++, и C # имеет небольшое преимущество в детерминизме, когда размеры данных превышают границы строк кэша. Однако за все это в конечном итоге пришлось заплатить, и это приводит к огромным затратам в виде недетерминированных падений производительности для C # из-за сборки мусора.

...