Inizierò in disaccordo con una parte della risposta accettata (e ben votata) a questa domanda affermando:
In realtà ci sono molte ragioni per cui il codice JITted verrà eseguito più lentamente di un programma C ++ (o altra lingua senza sovraccarico di runtime) correttamente ottimizzato, tra cui:
i cicli di calcolo spesi sul codice JITting in fase di esecuzione non sono per definizione disponibili per l'uso nell'esecuzione del programma.
eventuali percorsi caldi in JITter saranno in concorrenza con il codice per istruzioni e cache di dati nella CPU. Sappiamo che la cache domina quando si tratta di prestazioni e che i linguaggi nativi come il C ++ non hanno questo tipo di contesa, per definizione.
il budget temporale di un ottimizzatore di runtime è necessariamente molto più limitato di quello di un ottimizzatore di compilazione (come sottolineato da un altro commentatore)
Linea di fondo: In definitiva, si sarà quasi certamente in grado di creare una più rapida attuazione in C ++ di quanto si potrebbe in C # .
Ora, detto questo, quanto più velocemente non è davvero quantificabile, poiché ci sono troppe variabili: l'attività, il dominio del problema, l'hardware, la qualità delle implementazioni e molti altri fattori. Avrai eseguito dei test sul tuo scenario per determinare la differenza nelle prestazioni e quindi decidere se valga la pena lo sforzo e la complessità aggiuntivi.
Questo è un argomento molto lungo e complesso, ma penso che valga la pena menzionare per completezza che l'ottimizzatore di runtime di C # è eccellente ed è in grado di eseguire determinate ottimizzazioni dinamiche in fase di runtime che non sono semplicemente disponibili per C ++ con il suo tempo di compilazione ( statico) ottimizzatore. Anche con questo, il vantaggio è ancora in genere profondamente nella corte dell'applicazione nativa, ma l'ottimizzatore dinamico è la ragione del " quasi qualificatore certamente" di cui sopra.
-
In termini di prestazioni relative, sono stato anche disturbato dalle cifre e dalle discussioni che ho visto in alcune altre risposte, quindi ho pensato di intervenire e allo stesso tempo fornire supporto alle dichiarazioni che ho fatto sopra.
Una grande parte del problema con questi benchmark è che non puoi scrivere codice C ++ come se stessi scrivendo C # e ti aspetti di ottenere risultati rappresentativi (es. Eseguire migliaia di allocazioni di memoria in C ++ ti darà numeri terribili).
Invece, ho scritto un codice C ++ leggermente più idiomatico e confrontato con il codice C # fornito da @Wiory. Le due principali modifiche che ho apportato al codice C ++ sono state:
1) vettore utilizzato :: reserve ()
2) appiattito l'array 2d a 1d per ottenere una migliore localizzazione della cache (blocco contiguo)
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);
}
Tempo di esecuzione (Rilascio): Init: 124ms, Fill: 165ms
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);
}
Tempo di esecuzione (Release): Init: 398µs (sì, sono microsecondi), Fill: 152ms
Totale tempi di esecuzione: C #: 289ms, C ++ 152ms (circa il 90% più veloce)
osservazioni
La modifica dell'implementazione C # nella stessa implementazione di array 1d ha prodotto Init: 40ms, Fill: 171ms, Total: 211ms (il C ++ era ancora quasi il 40% più veloce ).
È molto più difficile progettare e scrivere codice "veloce" in C ++ che scrivere codice "normale" in entrambe le lingue.
È (forse) sorprendentemente facile ottenere scarse prestazioni in C ++; lo abbiamo visto con prestazioni di vettori senza prenotazione. E ci sono molte insidie come questa.
Le prestazioni di C # sono piuttosto sorprendenti se si considera tutto ciò che accade in fase di esecuzione. E quella performance è relativamente facile da accedere.
Ulteriori dati aneddotici che confrontano le prestazioni di C ++ e C #: https://benchmarksgame.alioth.debian.org/u64q/compare.php?lang=gpp&lang2=csharpcore
La linea di fondo è che C ++ ti dà molto più controllo sulle prestazioni. Vuoi usare un puntatore? Un riferimento? Stack memoria? Mucchio? Polimorfismo dinamico o eliminare l'overhead di runtime di una tabella con polimorfismo statico (tramite modelli / CRTP)? In C ++ devi ... er, arrivare a fare tutte queste (e di più) te stesso, idealmente in modo che la tua soluzione affronti al meglio il problema che stai affrontando.
Chiediti se in realtà desideri o hai bisogno di quel controllo, perché anche per l'esempio banale sopra, puoi vedere che sebbene ci sia un miglioramento significativo delle prestazioni, per accedere è necessario un investimento più profondo.