Properties - Vor- und Nachteile



  • @StephanSch: hast du auch den geJITeten Code mal angeschaut und nicht nur den IL-Code vom C#-Compiler? Nur mit diesem lassen sich wirkliche Aussagen treffen ob der Code zur Laufzeit nun optimiert wurde oder nicht.
    Der IL-Code ist zwar für vieles ein Indiz, aber nicht mehr.



  • StephanSch schrieb:

    Die genannten Nachteile stammen immerhin von einer Person, die auf dem amerikanischen Markt einen Bestseller zur CLR geschrieben hat.

    Good old Jeff ist normal schon eine Größe was .NET angeht... aber die meisten Punkte habe ich meiner Meinung nach schlüssig widerlegt und die anderen laufen auf: "OMG, Properties können Logik enthalten!!!" hinaus.

    Zum Thema Performance. Natürlich sind die Properties kritisch für die Performance, wenn sie nicht geinlined werden. Es sind immer noch Methoden.

    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.


  • Administrator

    GPC schrieb:

    StephanSch schrieb:

    Die genannten Nachteile stammen immerhin von einer Person, die auf dem amerikanischen Markt einen Bestseller zur CLR geschrieben hat.

    Good old Jeff ist normal schon eine Größe was .NET angeht... aber die meisten Punkte habe ich meiner Meinung nach schlüssig widerlegt und die anderen laufen auf: "OMG, Properties können Logik enthalten!!!" hinaus.

    Abgesehen davon:
    Wow! Die Person hat einen Bestseller geschrieben! Dann muss es ein super Experte sein! Siehe auch in C++, da ist Jürgen Wolf der beste Mann was C++ betrifft. Er hat schliesslich den Bestseller C++ von A bis Z geschrieben! 🤡 😃

    Ein Experte zeichnet sich nicht dadurch aus, dass er viele Bücher verkauft...
    Zudem muss ein Experte nicht immer recht haben.

    Grüssli

    PS: Ich will nichts gegen Jeffrey Richter sagen, nur gegen die Argumentation von StephanSch 😉



  • Dravere schrieb:

    gegen die Argumentation von StephanSch

    Gegen welche Argumentation?



  • 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!



  • 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 😉


Anmelden zum Antworten