Ik verbaas me werkelijk over het onvermogen van sommigen om te lezen.
Geen woord over dat deze test representatief zou zijn.
Natuurlijk is deze test niet representatief. Wie gaat er een array met null-objecten vullen en dan overal hashCode() op willen oproepen? Niemand. Het
voorbeeld geeft aan dat er situaties zijn waarin exceptions trager zijn dan een
equivalent stukje code die gebruik maakt van if-testen. Er werd opgemerkt dat
Bij mijn weten is een exception in Java misschien een paar instructies meer dan het retourneren van null of een status code, maar dat het duur is lijkt mij extreem zwaar overdreven.
Dat is onwaar. Vervolgens geef ik een voorbeeld waaruit blijkt dat de aanpak met exceptions bijna 100% trager wordt uitgevoerd. Wat mij betreft past het label "duur" dan wel. Dat exceptions minder performant (horen te) zijn, is uiteraard logisch want exceptions zijn bedoeld om uitzonderlijke situaties af te vangen. Niet om nominale werking van een code te programmeren. Dat is een van de redenen waarom ik mijn testvoorbeeld al zeker niet representatief zou noemen; het zou niet bij me opkomen om in een performance critical stukje code (bijvoorbeeld in een implementatie van hashCode) met exceptions te gaan werken ipv boolean tests omdat het wat makkelijker is.
ACM schreef op zondag 14 maart 2010 @ 11:31:
Als je de kans dat die exception optreedt veel kleiner maakt, is de kans zelfs groot dat die kwa performance wint. Overigens is het in Java heel lastig te zeggen of exceptions duur zijn of niet.
Absoluut. Indien er geen fout optreedt vermijd je de tests die je anders altijd zou uitvoeren.
Als ik de code hierboven pak en refactor naar 2 methodes die een daarna 10x uitgevoerd worden, is het resp. 847ms vs 678ms bij de 10e executie. Als ik echter de array aanpas om maar 1 element met null te hebben, dan is het verschil ineens een stuk trivialer. Uiteindelijk zijn ze dan bij mij dan op 3375 vs 3374ms.
Met vervolgens de exceptions helemaal weg komt de eerste uit op 3390ms en de if-aanpak op 4557ms... blijkbaar kan hotspot (Sun's 1.6.0_18 64bits jvm) dan ineens een stukje minder goed optimaliseren.
Hier is ook absoluut niets vreemd aan. Exceptions hebben "grote" overhead als de exception getriggered wordt, maar bijna geen overhead als er niets aan de hand is. Bij tests daarentegen heb je altijd de overhead. Een overhead die normaliter kleiner behoort te zijn dan het afhandelen van een exception. Dat wordt door mijn voorbeeld geïllustreerd.
Maar het moraal van het verhaal wordt hopelijk duidelijk zo: "Exceptions zijn duur" is tegenwoordig een onzinuitspraak.
Dankzij een geavanceerde VM. In puur geïnterpreteerde talen zal het performance verschil meetbaar zijn. Het verschil tussen exceptions en testen in Java met een moderne VM is natuurlijk erg klein. In een goed ontworpen stuk code horen exceptions geen bottleneck te vormen.
Dat gezegd hebbende moet je nu niet ineens overal Exceptions als de nieuwe performance-tool gaan misbruiken, maar je hoeft ze ook niet te mijden omdat ze de prestaties zouden verzieken

Kortom, ik schaar me hierin achter Janoz.
Ik ben van mening dat als je ze op een normale manier gebruikt (voor exceptioneel gedrag, niet voor nominaal gedrag) dat het dan inderdaad geen verschil maakt welke aanpak je gebruikt.
JKVA schreef op zondag 14 maart 2010 @ 19:00:
+1
Performance in Java bewijs je niet met een benchmark. Zelfs de conclusie die Nick The Heazk trekt (dat in zijn hele specifieke voorbeeld de ifelse sneller is), is onzinnig. Op mijn laptop performen beide implementaties bijvoorbeeld exact hetzelfde (server VM btw...).
Geeft op mijn systeem met Java 1.6 (OpenJDK) de volgende uitvoer:
Ik trek geen algemene conclusies. Het is perfect mogelijk dat dat verschilt van systeem tot systeem. Al zal het in dit geval waarschijnlijk liggen aan het verschil tussen de VMs.
Aan de andere kant, Nick The Heazk zijn gevoel dat exceptions trager zijn, lijkt mij wel valide, maar om een andere reden. Dynamic compilers zoals HotSpot zijn in principe geavanceerde pattern-matchers. Ze herkennen bepaalde constructies en vertalen deze naar efficiente code (inlining, dead code verwijderen, etc). Bij Sun moeten ze prioriteiten stellen, dus ze zullen eerst kiezen voor de optimalisaties waar veel mensen baat bij hebben. Hierdoor is de kans erg klein dat "vreemde" code efficienter zal worden bij een nieuwe Java release. Simpele, "naief geschreven", code zal dat misschien wel worden.
Er zijn verscheidene redenen waarom exception handling duurder is dan een boolean test. Het probleem met Java is dat er een VM en HotSpot optimizer tussen zit. In een compiled taal is het heel wat makkelijker (maar het blijft moeilijk) om een informed guess te maken over welke aanpak sneller zal werken. Een van de nadelen bij exception handling is dat je pipeline helemaal verstoord wordt. Een basale if-test, zoals in mijn voorbeeld, kan echter genieten van een goed branch prediction mechanisme. Niet meteen iets waar men in gewone server of desktop software mee bezig is, maar in mijn branche (HPC) dus wel. Dat was ook de aim van het voorbeeld; een geval waarbij de code met testen normaliter (in een taal als C++ of FORTRAN*) veel performanter zou zijn omdat na een paar iteraties de branch prediction perfect zal werken en een zeer hoge doorvoer zal bereikt worden. Bij de aanpak met exceptions daarentegen zal typisch een exception handling routine worden opgeroepen met mogelijk een reeks argumenten. Het nadeel daarvan is dat het voorspellen van een functieadres minder triviaal is dan branch prediction en over het algemeen ook minder goed zal werken. Zoals gezegd zit bij Java de VM er nog tussen, dus het verbaast me ook niet dat de resultaten kunnen verschillen. Indien we dit voorbeeld eens zouden testen op een oudere VM (zonder HotSpot) dan zou de aanpak met testen waarschijnlijk duidelijk beter werken.
Om deze reden vind ik performance op dit niveau geen valide argument bij het schrijven van code. Ik programmeer het liever zo duidelijk mogelijk. En ik bouw liever een expliciete null-check dan dat ik een NPE moet catchen. Je kunt toch niet voorspellen hoe de code zich @runtime gedraagt.
Absoluut.
Performance tuning op dit niveau is extreem speculatief. Een nieuwe (minor) versie, en de wereld kan er heel anders uitzien. Om die reden zeg ik, gewoon niet teveel aan denken. Getters en setters? Die zijn gratis. Defensief programmeren? Gewoon doen, dit is meestal ook gratis, want de JIT flikkert al je code gewoon weg.

Natuurlijk. Je hebt me nergens horen beweren dat ik een tegenstander van exceptions ben. Echter de bewering dat het "extreem zwaar overdreven" is om exception handling als "duur" te bestempelen vind ik dan weer overdreven

. Dankzij een erg goede dynamische optimizer (en goede programmeertechnieken) merk je het niet, maar exception handling is, op machineniveau, over het algemeen heel wat duurder dan problemen vermijden met een testje. Let wel, ik heb het hier over
exception handling. Wanneer de exception optreedt. Als je exception maar 1% of zelfs 10% van de tijd optreedt, zal er weinig aan de hand zijn.
YopY schreef op maandag 15 maart 2010 @ 09:20:
Overigens is het voorbeeld wat Nick geeft niet echt representatief in mijn mening - bij de eerste telling voorkom je de fout, bij de tweede vang je hem af, dwz de twee functies zijn logisch gezien niet gelijk.
Nogmaals, ik beweer niet dat het representatief is. De twee lussen zijn echter volledig equivalent. Een grappige opmerking overigens; bij de eerste lus laat ik de fout optreden en bij de tweede vermijd ik de fout. Is dat niet zo een beetje een van de belangrijke verschillen tussen werken met exceptions en testen

?
Het is de mijne. Het is geen benchmark, maar een voorbeeld van een situatie waarbij exceptions (op mijn systeem althans) niet goed werken. En tevens eentje waarvoor ze imo niet bedoeld zijn.
Daaraan kan je zien dat Java inderdaad bijna onmogelijk te benchmarken is, want kleine veranderingen in omstandigheden veranderen de uitkomst heel sterk.
Heel wat moeilijker dan in een compiled taal; daar moet je "enkel" weten wat de compiler met je code aanvangt, niet wat een dynamische compiler at runtime beslist

.
Hydra schreef op maandag 15 maart 2010 @ 10:22:
Komop zeg. Ik heb dat stuk gelezen maar mensen zijn nu druk aan het zoeken naar situaties waar het benchmarken van stukken code verkeerde resultaten oplevert. Als je een RL situatie probeert te testen is meten gewoon weten, zeker als je wet hoe hotspot werkt en de tests doet met verschillende settings. De Java VM biedt je niet voor niets de mogelijkheid zaken aan of uit te zetten d.m.v. de -XX opties.
Ik snap het probleem eigenlijk niet. Een aantal mensen hier reageert als een stier op een rode lap bij het zien van een stukje code. Plotseling is dat een representatieve benchmark die ik voorleg. Een verkeerd resultaat van een benchmark bestaat niet. Een stuk testcode kan hoogstens niet
representatief zijn voor hetgene men wil testen. Maar verkeerde resultaten bestaan niet (zolang je code correct is). Mijn voorbeeld geeft een situatie waarbij exceptions op mijn systeem met de OpenJDK VM voor Java 1.6 "duur" zijn. Prachtig. Eenieder die loopt te beweren dat ik met die code
de zaligmakende benchmark geef om exceptions met testen te vergelijken is spijkers in een uitgedroogd meer aan het zoeken.
terje7601 schreef op maandag 15 maart 2010 @ 10:51:
Lol

Het was vooral je "dat doe je toch 1-2-3 met een regex" die het 'm deed

Bovendien: er zijn best situaties waarin exceptions ten onrechte gebruikt worden (kan me niet meteen een voorbeeld bedenken, want ben zelf een exceptions-voorstander

), maar in je isInteger voorbeeld staat zelfs nergens een "throw ...", hoe kun je dan van exceptions-misbruik spreken?
De voorbeeld code die ik plaatste lijkt me een prachtig voorbeeld van misbruik, imo.
Over het benchmarken: tuurlijk kun je benchmarks schrijven om bijv. zoekalgoritmes te vergelijken of zo, maar je kunt m.i. simpelweg geen benchmark schrijven met als doel "toon aan dat exceptions duur zijn", dat was toch wat Nick probeerde.
Ik probeerde dat helemaal niet

. Ik toonde aan dat er situaties zijn waarbij exception handling gerust als duur kan bestepeld worden. Dat het geen representatief stuk code is, is zo klaar als een klontje. Ik beschouw het al een slecht stuk code. Maar andere mensen zullen het daar vast mee oneens zijn

.
.oisyn schreef op maandag 15 maart 2010 @ 12:40:
Klopt. En een van die pitfalls is dat je het probleem isoleert en dat benchmarkt, en daar vervolgens conclusies uit denkt te kunnen trekken. Benchmarken doe je met RL usecases, niet met geisoleerde testcases.
De conclusies die je uit een benchmark trekt zijn maar zo goed als de benchmark. Je kunt prachtige conclusies trekken uit een benchmark. Natuurlijk moet je benchmark wel representatief zijn voor het probleem dat je wil oplossen. Je zou de juiste conclusies moeten kunnen trekken uit je benchmark op voorwaarde dat je het probleem ook daadwerkelijk hebt geïsoleerd. Daar knelt het schoentje wel vaker. De verkeerde stukken code willen optimaliseren gaat je niet de gewenste resultaten opleveren. Maar daar vertel ik natuurlijk niets nieuws

. Benchmarken doe je met RL use cases of met representatieve use cases. Natuurlijk ga je de verkeerde conclusies trekken als je bv. de prestaties van een hashfunctie probeert vast te stellen door bijvoorbeeld uniform verdeelde elementen te kiezen, terwijl in je RL application object a met frequentie 99% voorkomt en de rest met uniforme kans. Als je op basis van zo'n benchmark conclusies wilt trekken voor je problem at hand dan komt het natuurlijk niet goed. Dat is gewoon een voorbeeld van een benchmark die niet representatief is; hij komt niet overeen met het probleem.
*) FORTRAN heeft geen exception handling mechanisme ... te duur

.
Performance is a residue of good design.