vrijdag 6 juni 2008

Snelheid + Schaalbaarheid .Net & Delphi

Als Delphi programmeur was ik nog een beetje sceptisch over .Net, vooral mbt de snelheid. Hiervoor heb ik in Delphi een klein programmaatje gemaakt, wat in 2 for loops wat strings heen en weer kopieert. Dit heb ik gemaakt in een thread, zodat ik ook makkelijk met meerdere threads kon testen. Dit programmaatje heb ik met Delphi 2006 zowel native als met Delphi.Net gecompileerd.

Test resultaten: Win32 vs .Net
Resultaten op een Intel Core2 Duo (dual core dus), MS .Net 2.0:
Delphi.Win32:
1 thread = 3s
2 threads = 6s + 6s

Delphi.Net:
1 thread = 2s
2 threads = 4s + 4s

.Net is dus sneller dan native! Bovendien schalen ze allebei niet goed, want beide keren bleef de CPU steken op zo'n 50% (dus geen goed gebruik van dual core).

Test resultaten: TopMM
Standaard gebruikt Delphi 200X de FastMM memory manager. Ik had al eens eerder een andere gezien, die wel goed zou moeten schalen: TopMM (http://topsoftwaresite.nl/Downloads/TopMemoryManager22.zip).

Resultaten:
Delphi.Win32, TopMM:
1 thread = 6s
2 threads = 6s + 6s

Tja, nu schaalt hij heel mooi (beide cores op 100%) maar de performance is wel slechter...

FastMM fix
Benieuwd waarom TopMM wel goed schaalt en FastMM niet, zat ik in de code te duiken. FastMM gebruikt een lock voor de memory pool, terwijl TopMM per thread een aparte pool heeft (geen locks dus nodig). Als eerste heb ik de lock eruit gehaald: nu kwam de snelheid ook op 2s! Maar goed, voor meerdere threads is die lock wel nodig. Toen heb ik even snel een hack gemaakt voor FastMM door oa een "threadvar" te gebruiken, zodat elke thread zijn eigen pool krijgt. Nu hebben beide threads 2s en 100% CPU, oftewel: Delphi.Win32 is even snel als .Net, maar bovenal: het schaalt beter!

Multi cores: nadeel voor .Net
Het grote nadeel nu aan .Net is dat je dit niet makkelijk zelf kunt fixen, wat in Delphi.Win32 wel makkelijk kan. Vooral met het oog op steeds meer cores per CPU is dit een slecht punt voor MS .Net.

MS CLR shared code
Benieuw hoe in .Net oa de string allocatie is, kwam ik via het volgende artikel:
http://selvasamuel.wordpress.com/2008/03/14/boxingunboxing-in-net/
op het idee om in de CLR code van Microsoft te duiken. Via het "Shared source" initiatief van MS kan iedereen een gedeelte van oa de CLR downloaden (maar niet aanpassen etc!):

Shared Source Common Language Infrastructure 2.0 Release
http://www.microsoft.com/downloads/details.aspx?FamilyId=8C09FD61-3F26-4555-AE17-3121B4F51D4D&displaylang=en

Als eerste heb ik in de ".Net Reflector" (http://www.aisto.com/roeder/dotnet/Download.aspx?File=Reflector)
de code opgezocht van mijn thread. Aan de hand hiervan naar de functie gesprongen (door te klikken in de code) voor het kopiëren van strings: System.String.Copy. Deze maakt oa gebruik van "FastAllocateString":
Deze functie was als volgt gedeclareerd (in C#):
[MethodImpl(MethodImplOptions.InternalCall)]
private static extern string FastAllocateString(int length);

"InterCall" betekent dat het in de .Net CLR zelf geïmplementeerd is. Aan de hand van de shared code van MS van de CLR heb ik gezocht op "FastAllocateString". Deze functie wordt weer doorgelinkt van "ECall::FastAllocateString" naar "JIT_TrialAlloc::GenAllocString(flags)".
Hierin wordt net als FastMM een lock gebruikt. Helaas is deze lock niet makkelijk aan te fixen, oa omdat de Shared Source CLR niet compleet is.

Geen opmerkingen: