Sunday, July 24, 2005
Microsoft Visual C++ 7.x still lousy at optimizations
I just answered a question from one of my customers about Visual C++ 7.x, Return Value Optimization and Named Value Optimization, and I think the answer could be interesting for a few of my readers as well (my Italian readers may want to look at my paper Return Value Optimization, Named Value Optimization e Costruttori Operazionali for a review of those concepts).
The sad truth is that the compiler is still lousy at optimizing object oriented code: that is, it's not any better than VC 6.0 on those specific optimizations.
More exactly:
- RVO is partially implemented. Copy construction is optimized away in trivial cases (returning anonymous objects built in the return statement without any further manipulation) but not when you build an anonymous object and change it via (e.g.) some operator.
- NVO is not implemented even in trivial cases.
Not long ago, I did some experiments comparing C++ and C# on some real-world numeric code I was writing. C# came really close to C++: actually, unless C++ code was nearly perfect :-), and without careful tweaking of compiling options for C++, C# was even faster. Of course there are little reasons for that (especially on numeric code), except the fact that the JIT knows the target processor. Of course, the easiest answer is that VC++ is just not so good at optimizing code. Seems like the easiest answer is probably true :-)).
I won't go into any (extremely deserved :-) anti-Microsoft rambling here, but this is exactly why so many programmers tend to hate them :-).
The sad truth is that the compiler is still lousy at optimizing object oriented code: that is, it's not any better than VC 6.0 on those specific optimizations.
More exactly:
- RVO is partially implemented. Copy construction is optimized away in trivial cases (returning anonymous objects built in the return statement without any further manipulation) but not when you build an anonymous object and change it via (e.g.) some operator.
- NVO is not implemented even in trivial cases.
Not long ago, I did some experiments comparing C++ and C# on some real-world numeric code I was writing. C# came really close to C++: actually, unless C++ code was nearly perfect :-), and without careful tweaking of compiling options for C++, C# was even faster. Of course there are little reasons for that (especially on numeric code), except the fact that the JIT knows the target processor. Of course, the easiest answer is that VC++ is just not so good at optimizing code. Seems like the easiest answer is probably true :-)).
I won't go into any (extremely deserved :-) anti-Microsoft rambling here, but this is exactly why so many programmers tend to hate them :-).
Comments:
<< Home
Nel post dici: "except the fact that the JIT knows the target processor". Quanto può essere il guadagno con ottimizzazioni specifiche per il processore target, e quanto si perde nel JIT compiling? Sono sicuro che hai fatto qualche esperimento anche su casi che non ammortizzano troppo il JIT come nel calcolo numerico!!
"Esperimenti" e' una parola grossa :-), non sono andato oltre qualche prova che comunque non puo' che confermare l'esperienza generale:
- su codice fortemente ripetitivo (vedi calcolo numerico) il JIT si ammortizza facilmente.
- su codice one-shot ovviamente no, ma va anche detto che molto codice one-shot non richiede tutta questa velocita' (solita legge di Pareto).
- lo startup di applicazioni in virtual machine, che sia Java o .NET, e' tragicamente lento, e i rimedi facili richiedono una commistione indesiderabile con il sistema operativo.
- in .NET come in Java, i maggiori controlli run-time, legati sia alla type safety che alla security, certo non aiutano ad andare piu' veloci. Qui c'e' un interessante trade/off che dovrebbe essere sotto il controllo di chi scrive il codice e/o di chi lo esegue, ma che al momento ha invece un impatto piu' o meno fisso.
- in .NET in particolare, le performance del rendering grafico delle Windows Form non sono esaltanti, anche a causa di un frequentissimo switch managed/unmananaged.
- in generale, il folklore d'uso di Java / .NET diventa sempre di piu' "alle prestazioni ci pensa l'hardware e un po' il JIT", che in parte e' anche vero, ma che alla fine porta ad una ampia diffusione di programmi scritti senza la minima attenzione alle prestazioni.
- la cosa tragica :-) e' ovviamente quando tale trascuratezza si riflette sugli algoritmi. In piu' casi senza la mia insistenza alcuni team avrebbero adottato (senza nemmeno porsi il problema) algoritmi quadratici dove era possibile [con uno sforzo maggiore] trovarne uno lineare (e dove i dati da processare potevano diventare parecchi, ma sul quadratico e' facile "diventare parecchi").
- uscendo un po' dalla domanda, va anche detto che le librerie (standard) di contenitori ed algoritmi di Java / .NET sono scarsotte se confrontate ad es. con STL, ed anche questo non aiuta a scrivere codice complessivamente performante.
- non va invece trascurato un elemento a favore del JIT che pochi considerano. In C++ ed altri linguaggi con compilazione nativa e' possibile, conoscendo il processore target, selezionare le opzioni di compilazione adatte ad ottimizzare per quel processore. Tuttavia la libreria run-time non sara' ottimizzata per quel processore. Una libreria JITted lo sara'. Non e' un elemento da poco, almeno se si usa la libreria.
- poi c'e' la solita storiella che in condizioni opportune un linguaggio con GC e' piu' performante di uno con allocazione/deallocazione "manuale". Che poi queste "condizioni opportune" si manifestino nella applicazioni reali, e' ampiamente da dimostrare :-).
Post a Comment
- su codice fortemente ripetitivo (vedi calcolo numerico) il JIT si ammortizza facilmente.
- su codice one-shot ovviamente no, ma va anche detto che molto codice one-shot non richiede tutta questa velocita' (solita legge di Pareto).
- lo startup di applicazioni in virtual machine, che sia Java o .NET, e' tragicamente lento, e i rimedi facili richiedono una commistione indesiderabile con il sistema operativo.
- in .NET come in Java, i maggiori controlli run-time, legati sia alla type safety che alla security, certo non aiutano ad andare piu' veloci. Qui c'e' un interessante trade/off che dovrebbe essere sotto il controllo di chi scrive il codice e/o di chi lo esegue, ma che al momento ha invece un impatto piu' o meno fisso.
- in .NET in particolare, le performance del rendering grafico delle Windows Form non sono esaltanti, anche a causa di un frequentissimo switch managed/unmananaged.
- in generale, il folklore d'uso di Java / .NET diventa sempre di piu' "alle prestazioni ci pensa l'hardware e un po' il JIT", che in parte e' anche vero, ma che alla fine porta ad una ampia diffusione di programmi scritti senza la minima attenzione alle prestazioni.
- la cosa tragica :-) e' ovviamente quando tale trascuratezza si riflette sugli algoritmi. In piu' casi senza la mia insistenza alcuni team avrebbero adottato (senza nemmeno porsi il problema) algoritmi quadratici dove era possibile [con uno sforzo maggiore] trovarne uno lineare (e dove i dati da processare potevano diventare parecchi, ma sul quadratico e' facile "diventare parecchi").
- uscendo un po' dalla domanda, va anche detto che le librerie (standard) di contenitori ed algoritmi di Java / .NET sono scarsotte se confrontate ad es. con STL, ed anche questo non aiuta a scrivere codice complessivamente performante.
- non va invece trascurato un elemento a favore del JIT che pochi considerano. In C++ ed altri linguaggi con compilazione nativa e' possibile, conoscendo il processore target, selezionare le opzioni di compilazione adatte ad ottimizzare per quel processore. Tuttavia la libreria run-time non sara' ottimizzata per quel processore. Una libreria JITted lo sara'. Non e' un elemento da poco, almeno se si usa la libreria.
- poi c'e' la solita storiella che in condizioni opportune un linguaggio con GC e' piu' performante di uno con allocazione/deallocazione "manuale". Che poi queste "condizioni opportune" si manifestino nella applicazioni reali, e' ampiamente da dimostrare :-).
<< Home





