Properties - Vor- und Nachteile
-
StephanSch schrieb:
GPC schrieb:
Sie sind ein Performancefaktor, den man in den allermeisten Fällen vernachlässigen kann. Der JIT-Compiler kann übrigens auch virtuelle Funktionen inlinen, wenn er feststellt, dass immer die gleiche aufgerufen wird. Unabhängig davon hängt dein ganz konkretes Performanceproblem nicht mit Properties zusammen.
Die meisten Fälle sind nunmal nicht alle Fälle und kann inlinen ist auch nicht wird inlinen.
Woran soll der Performanceunterschied sonst liegen? Wenn der Unterschied in der IL nur beim callvirt liegt? Erzähl es mir, ich bin neugierig!
Also du machst Performance-Aussage am IL-Code? Im anderen Board haben sie doch schon gesagt, dass du den geJittend Code (nativen Code) anschausen solltest, wobei ich selbst nicht weiß wie. Ähnlich Fall mit der JVM, jeder invokestatic kann/wird geinlined.
-
StephanSch schrieb:
GPC schrieb:
Sie sind ein Performancefaktor, den man in den allermeisten Fällen vernachlässigen kann. Der JIT-Compiler kann übrigens auch virtuelle Funktionen inlinen, wenn er feststellt, dass immer die gleiche aufgerufen wird. Unabhängig davon hängt dein ganz konkretes Performanceproblem nicht mit Properties zusammen.
Die meisten Fälle sind nunmal nicht alle Fälle und kann inlinen ist auch nicht wird inlinen.
Wenn ein Property als:
public class Foo { public IrgendeinTyp Bar { get; set; //Oder auch private set; } };
definiert ist, dann wird beim Zugriff auf Bar auch inlining durchgeführt. Nur bei virtuellen Properties kann es nicht immer gemacht werden.
Woran soll der Performanceunterschied sonst liegen? Wenn der Unterschied in der IL nur beim callvirt liegt? Erzähl es mir, ich bin neugierig!
Bezugnehmend auf den in myCSharp.de erstellten Thread liegt es an - wie dort schon festgestellt wurde - Range Check Elimination, die der Compiler nicht mehr durchführen kann. Du hast in dem Thread schon eine gewisse Beratungsresistenz gezeigt, obwohl dir ein Moderator dort deutlich erklärt, wo das Problem liegt und wie es zu lösen ist.
Das Problem mit deinem Code liegt bei der Klasse "TestTime2" nicht daran, dass die Property getLength so lahm wäre (lol, ich muss darüber immer noch lachen), sondern dass der JIT-Compiler kein RCE durchführen kann, da du nicht per array.Length auf die Längenangabe zurückgreifst sondern die Längenangabe UNABHÄNGIG vom Array holst.
Ich habe dir das mal umgeschrieben und dabei das Array erstmal geholt und dieses sowie dessen Länge dann in der Schleife benutzt. Im Prinzip ist die einzig wichtige Zeile: for (int i = 0; i < array2.Length; i++)class TestTime1 { public TestTime1() { } public double[] arr = new double[10000000]; } class TestTime2 { public TestTime2() { } public double[] Array { get { return arr; } } public int getLength { get { return arr.Length; } } private double[] arr = new double[10000000]; } class Program { static void Main(string[] args) { try { Stopwatch watch1 = new Stopwatch(); TestTime1 time1 = new TestTime1(); var array1 = time1.arr; watch1.Start(); for (int i = 0; i < array1.Length; i++) { array1[i] = i; i = (int)array1[i]; } watch1.Stop(); Stopwatch watch2 = new Stopwatch(); TestTime2 time2 = new TestTime2(); var array2 = time2.Array; watch2.Start(); for (int i = 0; i < array2.Length; i++) { array2[i] = i; i = (int)array2[i]; } watch2.Stop(); Console.WriteLine("Time for 1={0} and 2={1}", watch1.ElapsedMilliseconds, watch2.ElapsedMilliseconds); } catch (Exception ex) { Console.WriteLine(ex.Message); } } }
Edit: Und bevor du jetzt wieder mit "Aber du verwendest keine Properties" kommst, schau doch mal nach, wie die System.Array-Klasse die Eigenschaft "Length" definiert.. du wirst staunen, denn es ist ein Property!
-
Zeus schrieb:
Also du machst Performance-Aussage am IL-Code?
Sowohl am IL-Code, als auch an den gemessenen Zeiten, ja.
Zeus schrieb:
Im anderen Board haben sie doch schon gesagt, dass du den geJittend Code (nativen Code) anschausen solltest, wobei ich selbst nicht weiß wie. Ähnlich Fall mit der JVM, jeder invokestatic kann/wird geinlined.
Visual Studio hat dafür einen Disassembler und ich habe mir natürlich auch den JIT-Code angesehen.
i = (int)time2.Array[i]; 0000016a mov ecx,dword ptr [ebp-44h] 0000016d cmp dword ptr [ecx],ecx 0000016f call dword ptr ds:[005D26D0h] 00000175 mov dword ptr [ebp-6Ch],eax 00000178 mov eax,dword ptr [ebp-2Ch] 0000017b mov edx,dword ptr [ebp-6Ch] 0000017e cmp eax,dword ptr [edx+4] 00000181 jb 00000188 00000183 call 6E6775C0 00000188 movsd xmm0,mmword ptr [edx+eax*8+8] 0000018e cvttsd2si eax,xmm0 00000192 mov dword ptr [ebp-2Ch],eax
und
i = (int)array[i]; 0000016a mov eax,dword ptr [ebp-2Ch] 0000016d mov edx,dword ptr [ebp-50h] 00000170 cmp eax,dword ptr [edx+4] 00000173 jb 0000017A 00000175 call 6E5075C0 0000017a movsd xmm0,mmword ptr [edx+eax*8+8] 00000180 cvttsd2si eax,xmm0 00000184 mov dword ptr [ebp-2Ch],eax
-
GPC schrieb:
Und bevor du jetzt wieder mit "Aber du verwendest keine Properties" kommst, schau doch mal nach, wie die System.Array-Klasse die Eigenschaft "Length" definiert.. du wirst staunen, denn es ist ein Property!
Bisher staune ich noch über überhaupt nichts.
-
An GPC:
Eine Preisfrage für dich. Was geschieht in der folgenden Zeile?
cmp dword ptr [ecx],ecx
Falls unbekannt, einfach Beitrag auf der letzten Seite von mir lesen.
-
Btw eigentlich hab ich mich schon ausgeklinkt, aber über welche Zeitdimensionen reden wir?
-
14: object o = foo.MyFiled; 00000038 8B 45 F8 mov eax,dword ptr [ebp-8] 0000003b 39 40 04 cmp dword ptr [eax+4],eax 15: o = foo.MyProperty; 0000003e 8B 4D F8 mov ecx,dword ptr [ebp-8] 00000041 39 09 cmp dword ptr [ecx],ecx
genau der gleich Code (bis auf das unterschiedliche Register).
-
GPC schrieb:
Das Problem mit deinem Code liegt bei der Klasse "TestTime2" nicht daran, dass die Property getLength so lahm wäre (lol, ich muss darüber immer noch lachen), sondern dass der JIT-Compiler kein RCE durchführen kann, da du nicht per array.Length auf die Längenangabe zurückgreifst sondern die Längenangabe UNABHÄNGIG vom Array holst.
Es ist schon erstaunlich mit welcher Arroganz manche Personen im Netz unterwegs sind und wie sie versuchen andere Meinungen und Mitdiskutanten gezielt zu diskreditieren.
1. Das ist nicht "mein" Code, sondern eintriviales Beispiel.
2. Ich hatte bereits dem "Vollexperten" in dem besagten Forum erklärt das die RCE nicht allein verantwortlich für den Performanceeinbruch ist. Der Typ dort war sogar so dömlich, das er meine verlinkten Seiten mit den Aussagen von Jeffrey Richter verwechselte und mir dann auch noch unterestellte, ich würde das Prinzip der Datenkapselung nicht verstehen.
Am Ende begann er sogar meine Beiträge zu löschen, weil sie ihm nicht in das Weltbild passten.
Soviel zu Art. 5 GG.
Und jetzt erzählst du mir was diese Zeile kurz vor dem jb (Jump if Condition Is Met) macht?
cmp eax,dword ptr [edx+4]
-
ich finds lustig dass du, StephanSch, in beiden Foren den Leuten Arroganz vorhälts und dabei gar nicht bemerkst dass die Diskussion in beiden Foren ähnlich verläuft. Vllt. liegst ja an dir?
Wenn du im anderen Forum deren Regeln zugestimmt hast, und in denen steht AFAIK dass deine Beiträge auch gelöscht werden können, dann brauchst du nicht mit dem GG argumentieren.
Außerdem finde ich es erstaunlich wie du hier den "Vollexperten" darstellst, immerhin hat er dir helfen probiert (kostenlos und in siener Freizeit). Lies mal selbst deine Beiträge (die übrigens auch zusammengefasster geschrieben werden können, da nicht für jeden Gedanken ein neuer Beitrag sein muss) und versetzt dich in die Lage der Leute die dir helfen wollen. Können diese nachvollziehen was du meinst, wenn du unsauber zitierst und Folgerunden hinschreibst die einen Zusammenhang vermuten lassen?
-
StephanSch schrieb:
An GPC:
Eine Preisfrage für dich. Was geschieht in der folgenden Zeile?
cmp dword ptr [ecx],ecx
Falls unbekannt, einfach Beitrag auf der letzten Seite von mir lesen.
Kopier einfach mal meinen Code in eine Projektmappe, stell auf Release und schau dir die Ergebnisse an. Du solltest schon an den gleichen Zeiten (+- 1ms, je nach Lauf) feststellen, dass bei mir RCE funktioniert. Sorry, ich kann dir auch nicht weiter helfen. Wenn du's immer noch nicht verstehen willst, dann schlafe ich heute nacht auch ruhig wenn du alles auf öffentliche Felder umschreibst und Properties als Performancekiller bezeichnest.
StephanSch schrieb:
GPC schrieb:
Das Problem mit deinem Code liegt bei der Klasse "TestTime2" nicht daran, dass die Property getLength so lahm wäre (lol, ich muss darüber immer noch lachen), sondern dass der JIT-Compiler kein RCE durchführen kann, da du nicht per array.Length auf die Längenangabe zurückgreifst sondern die Längenangabe UNABHÄNGIG vom Array holst.
Es ist schon erstaunlich mit welcher Arroganz manche Personen im Netz unterwegs sind und wie sie versuchen andere Meinungen und Mitdiskutanten gezielt zu diskreditieren.
Stimmt. Stelle ich auch immer wieder fest.
1. Das ist nicht "mein" Code, sondern eintriviales Beispiel.
Du hast es gepostet und als Beispiel für den Performanceeinbruch durch Properties gebracht. Es ist dein Code. Ich habe den Code geändert und die Performance korrigiert.
2. Ich hatte bereits dem "Vollexperten" in dem besagten Forum erklärt das die RCE nicht allein verantwortlich für den Performanceeinbruch ist. Der Typ dort war sogar so dömlich, das er meine verlinkten Seiten mit den Aussagen von Jeffrey Richter verwechselte und mir dann auch noch unterestellte, ich würde das Prinzip der Datenkapselung nicht verstehen.
Ich kann nur über den von dir geposteten Code sprechen und bei dem war es einzig und allein RCE.
Am Ende begann er sogar meine Beiträge zu löschen, weil sie ihm nicht in das Weltbild passten.
Wird hier nicht passieren. Zukünftige Leser sollen von diesem Thread profitieren.
Und jetzt erzählst du mir was diese Zeile kurz vor dem jb (Jump if Condition Is Met) macht?
cmp eax,dword ptr [edx+4]
Da du das aus dem Visual Studio Disassembler hast, kommt der Code doch von einem Debug-Build. Wie soll der bitte aussagekräftig sein, besonders da RCE nur im Release-Modus verfügbar ist?!
-
Da du das aus dem Visual Studio Disassembler hast, kommt der Code doch von einem Debug-Build.
AFAIK geht das auch beim Release Build. Kann mich irren.
-
GPC schrieb:
Kopier einfach mal meinen Code in eine Projektmappe, stell auf Release und schau dir die Ergebnisse an. Du solltest schon an den gleichen Zeiten (+- 1ms, je nach Lauf) feststellen, dass bei mir RCE funktioniert.
Der Build ist ein Release-Build!
Und ich sage es noch einmal. Es macht für die CLR in der Laufzeitphase zur keinen Unterschied, ob du
for (int i = 0; i < time2.getLength; i++) { time2.Array[i] = i; i = (int)time2.Array[i]; }
oder
double[] array = time2.Array; for (int i = 0; i < array.Length; i++) { array[i] = i; i = (int)array[i]; }
schreibst. Die RCE wird bei beiden Codesegmenten durchgeführt.
Es hängt davon ab, ob der JIT-Compiler den Property-Aufruf inlined. Alles andere ist völlig irrelevant!
Wenn du es nicht glauben willst, führe beide Codesegmente oben aus.
-
Sealed die Klasse mal.
-
RCE wird in beiden Varianten NICHT durchgeführt.
-
theta schrieb:
Da du das aus dem Visual Studio Disassembler hast, kommt der Code doch von einem Debug-Build.
AFAIK geht das auch beim Release Build. Kann mich irren.
Stimmt, hast Recht.
@StephanSch
Ich klink mich jetzt aus, sollen andere die Diskussion weiterführen
-
Zeus schrieb:
Sealed die Klasse mal.
Hmm... die call-Befehle sind nun weg und die Laufzeiten sind auch gleich bei dem Beispielcode. Merkwürdig. Wieso werden die Aufrufe bei einer sealed Klasse geinlined, sonst aber nicht?
-
StephanSch schrieb:
Zeus schrieb:
Sealed die Klasse mal.
Hmm... die call-Befehle sind nun weg und die Laufzeiten sind auch gleich bei dem Beispielcode. Merkwürdig. Wieso werden die Aufrufe bei einer sealed Klasse geinlined, sonst aber nicht?
Eine sealed Klasse ist nicht virtuell. IMO muss ich GPC widersprechen, dass virtuelle Funktionen geinlined werden kann, dass kann nicht einmal ein C++ Compiler.
-
anton_zeiler schrieb:
RCE wird in beiden Varianten NICHT durchgeführt.
Das ist korrekt. Zu erkennen an
cmp eax,dword ptr [edx+4]
-
Zeus schrieb:
Eine sealed Klasse ist nicht virtuell.
Mir scheinen die ganzen unteren Optimierungsphasen äußerst instabil zu sein, was ihre Vorhersehbarkeit anbelangt.
Aber das war bei .NET auch zu erwarten. Man ist der CLR ausgeliefert. Da hat man bei C++ doch etwas mehr Kontrolle über den tatsächlichen Output.
-
Zeus schrieb:
Eine sealed Klasse ist nicht virtuell. IMO muss ich GPC widersprechen, dass virtuelle Funktionen geinlined werden kann, dass kann nicht einmal ein C++ Compiler.
Kommt auf den Fall an. Es ist durchaus möglich virtuelle Funktionen zu inlinen. Nicht in jeder Situation natürlich. Aber prinzipiell kann das ein C++ Compiler auch.
Die Frage ist ob das Objekt polymorph verwendet wird. Wenn ja, dann kann der Aufruf nicht geinlined werden (wir sind ja leider nicht in Eiffel) aber wenn es nicht polymorph verwendet wird dann ist es oft möglich zu inlinen.