Почему в этом бенчмарке C# уделывает C?

https://programming-language-benchmarks.vercel.app/csharp-vs-c

Или это строго подогнанный фейк?


Ответы (1 шт):

Автор решения: Mark Shevchenko

В данном случае для некоторых задач просто нет кода на C, поэтому вы и не видите C в списке. Чтобы убедиться, можно кликнуть по задаче и увидеть все примеры на всех языках.

Скажем, у binarytree нет примера на C.

Теперь немного более развёрнутый ответ. Формально, C# может оказаться быстрее C, если тесты позволяют исключить так называемый "прогрев".

В таких языках, как Java и C#, cкомилированные программы представляет собой байт-код. Когда то давно виртуальные машины просто интерпретировали его, что было достаточно медленно.

Для того, чтобы программы работали быстрее, разработчики виртуальных машин разработали технику, которая называется Just In Time-компиляция или, сокращённо, JIT.

При первом вызове любого метода виртуальная машина компилирует его в код физической машины, где выполняется программа. Это, собственно, и есть just in time, то есть, в нужное время. При последующих вызовах будет выполняться уже родной (native) машинный код.

Из-за JIT-компиляции только что запущенная программа работает медленнее, но потом постепенно разгоняется, потому что всё больше методов компилируются из байт-кода в машинный.

Это процесс образно называют "прогревом".

"Прогретая" программа будет работать быстро, как если бы она сразу была скомпилирована в машинный код. Во многих случаях это стандартный сценарий, например, когда вы "поднимаете сервер" на ASP.NET или запускаете Intellij IDEA, в которой работаете несколько часов.

Но так бывает не всегда, в частности, все консольные утилиты просто не успевают "прогреться".

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

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

Теперь обсудим, почему C# код может быть быстрее, чем код на C. Скорее всего, подобная ситуация будет возникать нечасто, всё-таки C "заточен" под производительность, в то время, как C# "заточен" под надёжность. Но если вдруг.

Абсолютно точное понимание картины вам даст изучение и сравнение машинного кода, который в конце концов будет сгенерирован компиляторами. Для C-компилятора вы можете указать флаг, который вместо объектного модуля сгенерирует код на языке Ассемблера (у разных компиляторов флаги разные, так что гуглите). Для C# вы можете использовать сайт SharpLab (выберите JIT Asm из выпадающего списка в поле Results).

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

  1. Если C# оказался быстрее, скорее всего использовались "прогретые" тесты.
  2. Тест может содержать простой код, в котором нет "тяжёлых" инструкций — виртуальных вызовов, работы со строками или массивами. В этом случай, машинный код C# будет не сложнее машинного кода на C.
  3. При компиляции в native-код мы не знаем, на каких точно процессорах будет выполняться программа — возможно, на устаревших, которые выпущены 5 лет назад. Производители процессоров постоянно придумывают новые наборы инструкций, чтобы ускорить выполнение. Может случиться так, что код на C скомпилирован для широкого круга процессоров, то есть фактически, он использует только инструкции пятилетней давности. JIT-компиляция выполняется на той же машине, где будет работать код, поэтому JIT-компилятор генерирует машинный код под текущий процессор. Если это новый процессор, то native код будет содержать самые современные быстрые инструкции.
  4. Код на C может активно выделять и освобождать память в куче, при этом куча фрагментируется (то есть блоки свободной памяти чередуются с блоками занятой памяти). Из-за фрагментации кучи выделение памяти может замедлиться. Это редкий сценарий и в реальной жизни поймать что-то подобное вряд ли не получится, но давайте просто порассуждаем о возможных вариантах. В .NET используется сборка мусора, поэтому память просто выделается, и довольно долгое время не освобождается. Если тесты работают недостаточно долго и оперативной памяти много, сборщик мусора может просто не запуститься и из-за этого программа на C# окажется быстрее.

Итог: обычно, код на C быстрее кода на C#, но в некоторых случаях может быть наоборот. Чтобы понять, в чём дело, сравнивайте машинные инструкции. Мы рассмотрели несколько возможных причин, но в вашем случае, причина может быть и другая.

→ Ссылка