Вопрос задан не совсем корректно, но я постараюсь на него ответить.
- Во-первых, нельзя просто говорить о эффективности программы, не уточная конкретные характеристики и бенчмарки (способы проверки характеристик программы). Так, если говорить о вычислениях с простыми типами, то программа на C# работает ни чуть не медленнее, чем на Си. Кроме того, при использовании памяти программа на C# не сильно хуже, а иногда и лучше, чем на Си. И это как раз-таки из-за сборщика мусора. На первый взгляд это кажется не очевидным, но, если Вы попытаетесь реализовать на Си алгоритмы так же, как на С#, то есть попытаетесь обойтись без явного контроля времени жизни выделяемой памяти, то Вы заметите, что память на Си фрагментируется гораздо быстрее. И, тем не менее, C# считается более медленным языком, потому как этот же самый сборщик мусора, оптимизирующий работу с памятью, значительно нагружает ЦПУ. Именно из-за этого на C#, в основном, пишут программы, которые не требуют максимальной оптимизации по времени, но выделяют в ходе своей работы гигантской количество объектов со слабо предсказуемым временем жизни. Например, себя могут так вести Desktop приложения со сложным пользовательским интерфейсом. При этом, если такой программе требуются быстрые вычисления, то она, скорее всего, будет использовать для них библиотеку на Си/С++. Так, например, устроены некоторые медиа плееры. GUI у них на C#, а кодеки Си или С++.
- Во-вторых, работающие на виртуальной машине программы работают немного медленнее из-за того, что докомпилируются прямо во время работы. Особенно это проявляется, если программа активно использует рефлексию, которая, как раз, и становится возможной из-за того, что программа запускается немного не докомпилированной и может переделывать себя по пути прямо во время работы. И это не только C# и даже не только .Net. Так же ведёт себя, например, и JVM вместе со всеми его языками: Java, Kotlin, Groovy и т.д.
- В-третьих, всеми этими же недостатками, как у C#, может обладать и программа на C++, если она собрана для .Net. То есть, если это не обычный C++, а C++/CLI.
PS: Тем, кто не верит в то, что на С++ могут быть проблемы с фрагментацией памяти, попробуйте в цикле выделить 1 ГБ памяти не целиком, а последовательно чередуя кусочки по 1 КБ и по 1 Байту. Потом освободите куски по 1КБ, а по 1 Байту оставьте и посмотрите сколько Вы держите памяти. Языки со сборщиком мусора легко переразместят память, при освобождении кусков по 1 КБ, хоть и завесят порядком программу на время этого переразмещения. На С++ же Вам для решения данной проблемы придётся использовать кастомные аллокторы, оптимизированные специально под подобные задачи. И то, не факт, что Вы сможете найти подходящие или, тем более, написать такой самостоятельно.
Мораль:
- Все языки эффективны, при использовании их в тех задачах, для которых они создавались.
- И не нужно натягивать сову на глобус. Сове это больно, а глобусу противно.