Detta är en fortsättning på en annan diskussion som jag bröt ut för att den hamnade vid sidan om det ursprungliga ämnet. Kolla på [Motsvarighet till vbCrLf] för att se var vi kom ifrån. "Förresten, har ni listat ut varför Faktiskt inte riktigt helt rätt, om jag tänker rätt... Hej. Jag förstod att du bara funderade, men du var ju på rätt spår... Hittade detta, det står att man inte riktigt kan lite på om den gör en metod inling eller ej. Låter som ett verktyg jag är intresserad av.. Leta nooooooooga :-) Här är programmet. Mattias i ditt första inlägg i detta forum så är det inte så konstigt att du får ett sådant resultat. För tiden att göra en append är större än att göra metod anropet till NewLine. Stacken är ju en LIFO! Så teoretiskt sätt om man tänker sig att stacken nu hanteras som en trave med LIFO så borde det ju inte vara en fördel om ett vörde läggs tidigt i stacken då stacken alltid plockar datan som ligger först. (Push och Pop), Men om nu \rt\n läggs tidigt i stacken och det gör att saker går snabbare så måste ju Stringbuilder ligga i heapen. Och detta gör att skillnaden blir liten då append metoden anropas från objketet som ligger i heapen. Sedan kör ju Jitten med inlining på metoder som inte ropar på andra metoder. jag kan ju vara helt ute men. Jag tänker inte ge mig in i diskussionen om ordningen på stacken spelar någon roll då jag inte har något att bidra med i detta. Att jag adderade med i, var pga att jag var orolig att JIT:en skulle optimera loopen. Alltså att den skulle bara gå genom en gång för att loopens innehåll blir alltid samma vi varje snurra. I Java så optimeras detta, var osäker på om det gjordes i .Net, så jag ville vara på säkra sidan. Såg att jag hade fått in 1 istället för i. När jag ändrade detta så tog Env...NewLine och newLine lika lång tid. Min teori om minnet kvarstår dock. Borde det inte vara bättre att ta upp optimeringar som har betydelse? Varför inte göra denna jämnförelse själv ? Jag tror vi har drivit detta så långt det går... Har du testat att skicka bara NL där NL är konstant Chr$(13) + Chr$(10). vbcrlf blir \r \n som är char 10 och 13 Fast i fel ordning vbCrLf = Chr$(13) & Chr$(10) och så har det varit sedan programmeringens barndom. Sven har rätt.. Jaja, Jag skrev i fel ordning. Mitt antydande var bara att dessa tecken blir 10 resp 13. Vad är det för skitsnack. Grejen är den att Environment.NewLine returnerar just \r\n visst kan man om man vill Oki på det. Fantomen är hård mot dom hårda :-)Prestandajämförelse mellan vbCrLf och Environment.NewLine
Jag lade till det fall som Johan beskrev, så mina anrop ser nu ut som:
<code>
' Fall A
For i = 0 To 2000000
sb1.Append("hej")
sb1.Append(vbCrLf)
sb1.Append("hopp")
Next
' Fall B
For i = 0 To 2000000
sb1.Append("hej")
sb1.Append(Environment.NewLine)
sb1.Append("hopp")
Next
' Fall C
Dim nl As String = Environment.NewLine
For i = 0 To 2000000
sb1.Append("hej")
sb1.Append(nl)
sb1.Append("hopp")
Next
</code>
10 testkörningar gav följande resultat (alla tider i ms).:
Fall A Fall B Fall C
===============================================
2186 2066 2068
2082 2102 2025
2026 2021 2044
2015 2151 2145
2030 2020 2022
-----------------------------------------------
2087 2072 2060 Medeltider
Skillnaden mellan dessa fall är följande när man tittar på IL-koden.
Fall A:
IL_002a: ldstr "\r\n"
IL_002f: callvirt instance class [mscorlib]System.Text.StringBuilder [mscorlib]System.Text.StringBuilder::Append(string)
Fall B:
IL_0080: call string [mscorlib]System.Environment::get_NewLine()
IL_0085: callvirt instance class [mscorlib]System.Text.StringBuilder [mscorlib]System.Text.StringBuilder::Append(string)
Fall C:
IL_00dc: ldloc.1
IL_00dd: callvirt instance class [mscorlib]System.Text.StringBuilder [mscorlib]System.Text.StringBuilder::Append(string)
Det som skiljer är alltså hur man laddar "\r\n" på callstacken. I fall A används ldstr, i fall B anropas en metod (call) och i fall C använd ldloc för att ladda en lokal variabel. ldstr används även i System.Environment::get_NewLine() eftersom dess IL kod är som följer:
IL_0000: ldstr "\r\n"
IL_0005: ret
Sammanfattningsvis så kan man nog säga att i det stora hela, om man gör något riktigt i sina loopar så spelar den ingen roll om man väljer vbCrLr eller Env.NewLine.
Förresten, har ni listat ut varför <code>s = "hej" + Environment.NewLine + "hopp"</code> var så mycket långsammare än <code>s = "hej" + vbCrLf + "hopp"</code>?
/MattiasSv: Prestandajämförelse mellan vbCrLf och Environment.NewLine
<code>
s = "hej" + Environment.NewLine + "hopp"
</code>
var så mycket långsammare än
<code>
s = "hej" + vbCrLf + "hopp"
</code>"
Nej inte riktigt, har inte tittat så mkt på detta, men om man tar det hela i ett teoretiskt stadie så kanske det kan vara sä här:
I falla A så fyller den stängen ss med Hej sedan måste den ropa på en metod,samt därefter lägga till hopp. Vilket sker under runtime.
Typ:
<code>
IL_....: call string [mscorlib]System.Environment::get_NewLine()
</code>
I fall B så ersätts vbcrlf med char 10 samt 13. Här utförs ingen metodanrop. Utan omvanlingen bör sker vid kompilering.
typ:
<code>
IL_....: ldstr "\r\n"
</code>
Ganska logiskt att Fall B då blir snabbare än fall A. Då Fall A kör metod under Runtime där Fall B slipper.
Tar vi loop exemplet hade jag valt Fall C.
<code>
Dim nl As String = Environment.NewLine
For i = 0 To 2000000
sb1.Append("hej")
sb1.Append(nl)
sb1.Append("hopp")
Next
</code>
Då fall C gav bättre tid (dock var skillnaden så liten, hur det blir med 100 samtliga användare har jag personligen inte testat.) Sedan så ger Fall C två direkta fördelar.
<code>
Dim nl As String = Environment.NewLine
</code>
Ger platforms-oberoendehet. Samt så blir implementationen lite snyggare och mer lättanvänd. Vill man byta ut nl till något helt annat så går det rätt smidigt, särskilt om du skulle återanvända nl på flera ställen i din kod.
Detta endast om jag vill ha min kod platforms-oberoende. Annars är kan man lika gärna köra vbcrlf eller lägga till \r\n med en gång.
//Johan NSv: Prestandajämförelse mellan vbCrLf och Environment.NewLine
<code>
s = "hej" + vbCrLf + "hopp"
</code>
genererar
<code>
IL_000d: ldstr "hej\r\nhopp"
IL_0012: stloc.2
</code>
medans
<code>
s = "hej" + Environment.NewLine + "hopp"
</code>
genererar
<code>
IL_0013: ldstr "hej"
IL_0018: call string [mscorlib]System.Environment::get_NewLine()
IL_001d: ldstr "hopp"
IL_0022: call string [mscorlib]System.String::Concat(string,
string,
string)
IL_0027: stloc.2
</code>
Skillnaden är alltså att i det första fallet så är strängen helt klar, alltså kan man en massa anrop per sekund. I andra fallet så utförs 3 anrop till ldstr (get_NewLine var ju enbart en ldstr) samt ett anrop till Concat, så skillnaden är ganska stor.
Jag skulle gissa att JIT-kompilatorn optimerar bort själva anropet till get_NewLine när den omvandlar koden till maskinkod, det är väl detta som kallas inlining.
/MattiasSv: Prestandajämförelse mellan vbCrLf och Environment.NewLine
Jag testade inget så jag tog bara IL koden du hade på din kommentar innan. Drog bara en snabb teori. Men jag står fortfarande fast vid att en färdig sträng i IL går snabbare än calling i IL. Dock vet jag inte hur JIT:en better sig under runtime. Men om den skulle ändra newline till \r\n så måste den ju utföra kompileringen två ggr?
Först köra IL koden, sedan andra gången minnas vad NewLine returnerade m.m. Frågan är om jitten fungerar så?
Jag tror inte den kommer att minnas newline utan ropa på den varje gång.
För hur vet jiten att newline metoden inte är dynamisk och kanske ändras under runtime? Säg att newline metoden ev läser i en XML-fil där du satt en NewLine tag med \r\n och sedan helt plötsligt ändrar den till bara \n då kommer ju även newline metoden att ge \n. Vilket bör betyda att calling alltid utförs?
Jag är inten JIT pro, utan detta är enbart teorier. Eftersom skillnaden var så stor mellan calling samt incke där stäången var färdigkompilerad så bör den ju anropa metoden om och om igen.
//Johan NSv: Prestandajämförelse mellan vbCrLf och Environment.NewLine
Vad gäller inlining så handlar det inte alls om att minnas vad som har returnerats, utan om att elimiera själva anropet. Om du kan C++ så vet du att det finns ett keyword som heter inline, det indikerar att kompilatorn inte behöver göra en call utan kan "kopiera" in den färdiga koden istället för anropet. Dvs samma kod körs med man måste inte genomföra ett call-anrop och push argument på stacken.
Om koden innehåller inläsning av XML-fil så kommer alltså precis samma sak genomföras varje gång, fast utan call-anropet.
Så om IL koden ser ut såhär:
<code>
ldstr "hej"
call string [mscorlib]System.Environment::get_NewLine()
ldstr "hopp"
call string [mscorlib]System.String::Concat(string,
string,
string)
stloc.2
</code>
och getNewLine implementeras som
<code>
ldstr "\r\n"
ret
</code>
så kan JIT att generera kod precis som om det stod IL kod såhär:
<code>
ldstr "hej"
ldstr "\r\n"
ldstr "hopp"
call string [mscorlib]System.String::Concat(string,
string,
string)
stloc.2
</code>
Viktigt med KAN här, detta är beslut som JIT gör när en viss metod kompileras. Läs mer om när en metod "inlinas" på under rubriken Method Inlining i http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dndotnet/html/highperfmanagedapps.asp.
/MattiasSv: Prestandajämförelse mellan vbCrLf och Environment.NewLine
Method Inlining
There is a cost associated with method calls; arguments need to be pushed on the stack or stored in registers, the method prolog and epilog need to be executed and so on. The cost of these calls can be avoided for certain methods by simply moving the method body of the method being called into the body of the caller. This is called Method In-lining. The JIT uses a number of heuristics to decide whether a method should be in-lined. The following is a list of the more significant of those (note that this is not exhaustive):
*Methods that are greater than 32 bytes of IL will not be inlined.
* Virtual functions are not inlined.
* Methods that have complex flow control will not be in-lined. Complex flow control is any flow control other than if/then/else; in this case, switch or while.
* Methods that contain exception-handling blocks are not inlined, though methods that throw exceptions are still candidates for inlining.
*If any of the method's formal arguments are structs, the method will not be inlined.
I would carefully consider explicitly coding for these heuristics because they might change in future versions of the JIT. Don't compromise the correctness of the method to attempt to guarantee that it will be inlined. It is interesting to note that the inline and __inline keywords in C++ do not guarantee that the compiler will inline a method (though __forceinline does).
Property get and set methods are generally good candidates for inlining, since all they do is typically initialize private data members.
"HINT Don't compromise the correctness of a method in an attempt to guarantee inlining."
Detta verkar intressant, skall nog kolla mer innom området. Dock verkar det som newline alltid anropas, då ditt test gav så stor skillnad. Just nu kan jag inte komma på en annan lösning varför. Om du får något svar så vore det kul o få veta.
Jag hittade en gång ett verktyg som analyserade ens kod under runtime. Visade statistik, IL kod red för rad vad som utfördes m.m. kommer dock inte ihåg vilket det var, men det var ett trevligt program för att se hur Runtimen hanterade ens kod.
Skall se om jag kan hitta det igen.
//Johan NSv: Prestandajämförelse mellan vbCrLf och Environment.NewLine
---
<b>Andreas Håkansson
Student of Software Engineering</b>Sv: Prestandajämförelse mellan vbCrLf och Environment.NewLine
AQtime .Net heter det.
Dock kan du bara ta ner en DEMO.
http://www.automatedqa.com/downloads/aqnet.asp
//Johan NSv: Prestandajämförelse mellan vbCrLf och Environment.NewLine
Här är ett test som bevisar att "\r\n" är betydligt snabbare än Environment.NewLine:
<code>
for( int i = 0; i < 2000000; i++ )
{
s = "hej" + "\r\n" + "hopp" + i; // 3000 msec
s = "hej" + Environment.NewLine + "hopp" + i; //5000 msec
s = "hej" + newLine + "hopp" + 1; //4000 msec.
}
</code>
Men detta var intresant.
<code>
for( int i = 0; i < 2000000; i++ )
{
s.Append("\r\n"); //617 msec
s.Append(Environment.NewLine); //599 msec
s.Append(newLine); //578 msec
}
</code>
Resultaten på de tester vi har gjort beror antagligen på vart i stacken JIT:en lägger värderna. Jag tror att newLine som går snabbast i testen ovan, gör detta enbart för att varibalen läggs längre upp i stacken än i de andra fallen. Vilket betyder att den kan plockas fram fortare än annan data som lagras i längre ner. Samma sak med Env..NewLine. Beroende på vart i minnet data lagras så påverkar det tiden att hämta upp datan. Att resulten är så lika beror oxå på att Append är en dyrare operation än tex Env..NewLine som är en billig operation.
/Fredrik NSv: Prestandajämförelse mellan vbCrLf och Environment.NewLine
Newline ger bara en retur på en valur type med värde \r\n.
Att ropa på Append metoden på ett objekt som ligger i heapen jämför met en valutype som ligger på stacken måste bara ge en stor skillnad i prestanda. (om vi räknar millisekunders skillnad.) trotts inlinging.
//Johan NSv: Prestandajämförelse mellan vbCrLf och Environment.NewLine
1) jitten sköter bara konverteringen från cil till maskinkod , och aktiveras bara första gången något anropas.
2) alla värden plockas från stacken när man anländer i ett anrop , ingen kod börjar exekveras förrens alla params är plockade från stacken , och därför spelar det ingen roll om en param packas först eller sist.
eller?
//Roger
[edit]
en annan sak som var ganska intressant , om man använder en highres timer och kör koden i release läge utanför vs.net så fick jag följande värden:
long max=4000000;
for( long i = 0; i < max; i++ )
{
// s.Append("\r\n"); //3793901
// s.Append(Environment.NewLine); //3793105
// s.Append(newLine); //3799735 (intern)
s.Append(newLine); //3805015 (ej intern)
}
där siffrorna är i ticks.
alltså , alla sätten gick ganska så exakt lika snabbt att göra..
körde man däremot innifrån vs.net så diffade det ganska mycket på värdena...
(vs.net krokar på massa extra tjäbbel på processerna även om man startar i releaseläge i vs.net)Sv: Prestandajämförelse mellan vbCrLf och Environment.NewLine
Däremot tänkte jag kommentera Fredriks test. Jag noterar att du får en prestandaskillnad mellan att anropa NewLine varje gång eller att spara den. Jag tror att det är en felaktig siffra, iaf enligt testerana jag gjort.
Felet ligger i att du i ena fallet gör "+ i" och i det andra "+ 1". Min tester visar att det tar ca 30% längre tid med +i istället för +1, vilket är ungefär vad din siffra visar. Det är alltså ej mer effektivt att lagra newline än att anropa varje gång, i detta specifika fall alltså.
Även om jag skriver ner serier av mätningar så får jag ingen direkt skillnad mellan dina tre Append fall. Visst skiljer de sig åt, men för mig skiljer de sig lika mycket åt mellan omstarter av programmet.
Resultatet beror alltså huvudsakligen på om vi gör strängsammanslagning med 2 ("hej\r\nhopp" + i) eller 4 ("hej" + newline + "hopp" + i) delsträngar och inte hur man får fram "\r\n". Precis som någon av er sade tidigare...
/MattiasSv: Prestandajämförelse mellan vbCrLf och Environment.NewLine
<code>
for( int i = 0; i < 2000000; i++ )
{
//s = "\r\n"; //16
//s = Environment.NewLine; //19
//s = newLine; //14
}
</code>
Att s = newLine går snabbast här är p.g.a. att s kommer att peka på newLines minnes poition för att string är en referens typ.
Sedan beroende på i vilken ordning man slår i hop strängar så påverkar det prestandan.
tex:
s = "\r\n" + i + "hej"
tar lika lång tid som
s = newLine + i + "hej".
Men när jag kör
s = "h" + "\r\n" + i + "hej"
så går detta ca: 30% fortare än
s = "h" + newLine + i + "hej"
Att det går sämre med newLine i det sista testet kan bero på att i det första fallet så pekar s på newLines minnesposition sedan när man + på i så allokeras s om och i läggs till. Medans i det andra fallet så sätts s till "h" sedan hämtas newLines värde från heapen och gör en kopia på den för att sedan lagras i s, vilket är en kostsammare operation än att enbart lägga till "\r\n" som inte behöver göra en extra tripp till heapen för att hämta data.
hmm, gjorde en ny test som fick mig att funder lite.
Om jag gör en loop som för varje gång rensar s så går + mkt snabbare än att köra
append. Men när jag hela tiden adderar s med s så går Append betydligt fortare, vilket var väntat.
<code>
s = "h" + "\r\n" + i + "hopp"; //3321
s.Append("h").Append("\r\n").Append(i).s.Append("hopp"); //5863
</code>
<code>
s += "h" + "\r\n" + i + "hopp";
s.Append("h").Append("\r\n").Append(i).s.Append("hopp");
</code>
Så ska man göra en loop som under loopens gång bygga ihop en stor sträng så går Append fortare. Men ska man i varje loop bara slå i hop en ny sträng varje gång så går det långsammare med Append. Självklart beror det på hur stor sträng man ska bygga. Ju mer man adderar till s ju långsammare går s, medans Append kommer att hålla ungefär samma tid.
Vet igenltigen inte vart jag vill komma med allt detta, bara göra olika tester för att se vad man får för resultat.
Tack för mig!
/Fredrik NSv: Prestandajämförelse mellan vbCrLf och Environment.NewLine
Varför inte plocka fram sätt att optimera anrop till ado eller operationer som påverkar mer än vad just dessa gör. Känns lite overkill att tjäna in några milliskunder var 20000 anrop eller vad det nu är.
KAn ni inte jämföra hirarkiska recordset mot en motsvarande loop. Där är en optimering man kan spara tid på.Sv: Prestandajämförelse mellan vbCrLf och Environment.NewLine
---
<b>Andreas Håkansson
Student of Software Engineering</b>Sv: Prestandajämförelse mellan vbCrLf och Environment.NewLine
Precis som Fredrik skriver så är ju dessa tester gjorda mycket för att se vad som händer, inte för att det har praktisk betydelse i normala program. Diskussion för diskussionens skulle helt enkelt :-) Alltid kommer det fram något nytt!
Angående andra prestandatester, som kanske har lite mer praktiskt betydels, så kan ju den som känner för det skriva en artikel och skicka in. Jag har en känsla av att eventuella slutsatser i en sådan artikel inte kommer undgå granskning...
/MattiasSv: Prestandajämförelse mellan vbCrLf och Environment.NewLine
Sv: Prestandajämförelse mellan vbCrLf och Environment.NewLine
//Johan NSv: Prestandajämförelse mellan vbCrLf och Environment.NewLine
Sv: Prestandajämförelse mellan vbCrLf och Environment.NewLine
\r står för return ,som är 13
\n står för new line , som är 10
cr = carrage return
lf = line feed
//RogerSv: Prestandajämförelse mellan vbCrLf och Environment.NewLine
Sedan om r = 13 och n = 10 tycker inte jag spelade så stor roll i detta sammanhang då jag personligen tycker svens inlägg inte hörde hit.
Hoppas slippa se mer onödiga inlägg här.
//Johan NSv: Prestandajämförelse mellan vbCrLf och Environment.NewLine
>Prestandajämförelse mellan vbCrLf och Environment.NewLine> lyder rubriken.
Då har väl för fae... Chr$(13) och Chr$(10) relevans,speciellt i en stor loop.
Om man tillverkar en konstant NL så vet man att man gjort den absolut snabbaste lösningen (gäller VB).
Tror märkväl ! Tror att NL är snabbare än vbCrLf i en loop.
Om man använder vbCrLf så måste programmet springa och fråga tolken varje gång.Vad betyder vbCrLf ?
>Hoppas slippa se mer onödiga inlägg här.
Spela inte marig,Vi skriver vad fae.. Vi vill utan att fråga dig !!!Sv: Prestandajämförelse mellan vbCrLf och Environment.NewLine
slänga in char(13) resp char(10) i sin stäng. Grejen är den att kompilatorn omvandlar vbcrlf åt dig vilket betyder att du inte behöver ta hänsyn till vad den gör eller ej.
IL koden blir:
<code>
IL_0001: ldstr "\r\n"
</code>
När du angett vbcrlf
Vilket betyder att den inte alls måste tolka vbcrlf hela tiden detta fixar kompilatorn åt dig.
Visst du kan gå hur djupt ner du vill i programmeringen. Man kan styra om allt till byte nivå om man vill.
Ja visst får du skriva vad du vill, men jag upplever alltid en grinig ton från din sida så fort du skriver något. Eller nerklanknings känlan från dig.
"och så har det varit sedan programmeringens barndom." <-- Jag vet, så du behövde inte anntyda detta som om man verkade dum.
Vi kanske fick en dålig start?
//Johan NSv: Prestandajämförelse mellan vbCrLf och Environment.NewLine