Ein paar generelle fragen...



  • hallo zusammen
    ich habe nur schnell ein paar kurze fragen.

    1. warum verwendet man nicht immer inline funktionen? dies soll ja scheinbar viel schneller sein. es muss aber einen grund haben warum man inline funktionen eher selten und nicht immer als sollche deklariert. blos welchen grund hat es?

    2. ich bin der meinung man sollte bei ganzzahlen werte wirklich auch den daten typ für die entsprechende grösse nehmen. also eine zahl 231 gehört eigentlich zu einem short int. warum aber sieht man meistens nur int?

    3. wenn in einem if/else block fast identischer code vorkommt, lagere ich den identischen code in eine neue funktion aus, weil es meiner meinung nach schwachsinn ist, identische codezeilen in einem if/else block zu schreiben. selbst dann wenn es sich jeweils im nur 3-5 zeilen handelt. was denkt ihr darüber? gibt es eine bessere lösung?

    4. sollte man vor dem setzen eines wertes in einer if bedingung erst überprüfen ob es notwendig ist? oder macht das nichts aus? kleines beispiel:

    if(var != "") var = "";
    

    5. gibt es einen speziellen syntax um folgendes beispiel besser zu schreiben, so dass man var2 nicht erneut schreiben muss?

    var1 = (var2 > 0) ? var2 : "";
    


  • T@@SSy schrieb:

    1. warum verwendet man nicht immer inline funktionen?

    Weil es nicht immer möglich ist und weil es den Code größer macht. Oft muss man abwägen zwischen Performance und Executable Größe.

    T@@SSy schrieb:

    2. ich bin der meinung man sollte bei ganzzahlen werte wirklich auch den daten typ für die entsprechende grösse nehmen. also eine zahl 231 gehört eigentlich zu einem short int. warum aber sieht man meistens nur int?

    Faulheit?

    T@@SSy schrieb:

    3. wenn in einem if/else block fast identischer code vorkommt, lagere ich den identischen code in eine neue funktion aus, weil es meiner meinung nach schwachsinn ist, identische codezeilen in einem if/else block zu schreiben. selbst dann wenn es sich jeweils im nur 3-5 zeilen handelt. was denkt ihr darüber? gibt es eine bessere lösung?

    Kann man so generell nicht beantworten; kommt auf den konkreten Fall an. Wenn es Sinn macht eine Funktion daraus zu machen: klar. Aber wenn die Funktion nur an 2 Stellen aufgerufen wird und nur existiert um ein paar Zeilen Redunanz zu sparen und dann vermutlich auch noch einen recht nichtssagenden Namen hat, würde ich es eher lassen.

    T@@SSy schrieb:

    4. sollte man vor dem setzen eines wertes in einer if bedingung erst überprüfen ob es notwendig ist? oder macht das nichts aus? kleines beispiel:

    if(var != "") var = "";
    

    Kommt drauf an ob es notwendig ist. Wenn der alte Wert unwichtig ist, kannst du ja auch einfach so zuweisen.

    T@@SSy schrieb:

    5. gibt es einen speziellen syntax um folgendes beispiel besser zu schreiben, so dass man var2 nicht erneut schreiben muss?

    Denke nicht.



  • T@@SSy schrieb:

    1. warum verwendet man nicht immer inline funktionen? dies soll ja scheinbar viel schneller sein. es muss aber einen grund haben warum man inline funktionen eher selten und nicht immer als sollche deklariert. blos welchen grund hat es?

    Die meisten Compiler ignorieren das inline Schlüsselwort sowieso und machen es automatisch, wenn es sinnvoll ist.

    5. gibt es einen speziellen syntax um folgendes beispiel besser zu schreiben, so dass man var2 nicht erneut schreiben muss?

    var1 = (var2 > 0) ? var2 : "";
    

    Was soll das sein? Zahl oder String?

    this->that schrieb:

    T@@SSy schrieb:

    2. ich bin der meinung man sollte bei ganzzahlen werte wirklich auch den daten typ für die entsprechende grösse nehmen. also eine zahl 231 gehört eigentlich zu einem short int. warum aber sieht man meistens nur int?

    Faulheit?

    Nein, int ist normal so groß wie die Hardware-Register mit denen gerechnet wird. Hängt natürlich auch vom Compiler ab, aber so ist es am sinnvollsten, weil dann keine zusätzlichen operationen durchgeführt werden müssen um z.B. auf einem 32 Bit System einen 8 Bit Datentyp korrekt zu simulieren.



  • T@@SSy schrieb:

    1. warum verwendet man nicht immer inline funktionen? dies soll ja scheinbar viel schneller sein. es muss aber einen grund haben warum man inline funktionen eher selten und nicht immer als sollche deklariert. blos welchen grund hat es?

    Die Annahme das dies viel schneller sein soll ist falsch. Es _kann_ viel schneller sein, hängt aber von der Funktion ab. Durch inline spart man den Overhead eines Funktionsaufrufs, was sich bei einer kleinen Funktion deutlich bemerkbar macht. Bestes Beispiel dafür wäre eine Getter-Funktion die oft eh nur eine einzige Zeile Code hat.

    Anders ist das aber bei umfangreicheren Funktionen die komplexere Sachen machen. Da fällt der Overhead des Funktionsaufrufs überhaupt nicht mehr ins Gewicht und in Folge würde es dann auch aus Performancesicht gar keinen Unterschied machen ob man die Funktion inlined oder nicht.

    Der Nachteil von inline ist, daß der auszuführende Code größer wird, weil die Funktion ja immer komplett an die Stelle kopiert wird wo sonst nur der Funktionsaufruf steht. Wieder, bei kleinen Funktionen macht das nichts aus (Im Beispiel des Getters ist es sogar irrelevant), aber bei komplexeren Funktionen fällt das schon ins Gewicht.

    Größerer Code aber kann dazu führen das der Code nciht mehr komplett in die Prozessor-Caches passt (L1, L2 usw) mit der Folge das der Programmablauf öfter als nötig aus dem Hauptspeicher nachgeladen werden muß. Das wiederum kann die Gesamtperformance erheblich negativ beeinflussen.

    Falsch angewandtes inlining kann also Dein Programm auch erheblich verlangsamen.

    _Deswegen_ ist inlining optional und sollte vom Programmierer nur da explizit gesetzt werden, wo er genau weis was er tut, ansonsten fällt sowas unter premature Optimizing. In der Regel weis der Compiler selbst viel besser wann inlining sinnvoll ist und wann nicht und gerade wenn Du als Anfänger die Zusammenhänge noch nicht begreifst tuest Du besser daran, solche Sachen erstmal dem Compiler zu überlassen.

    Ausserdem... Schonmal versucht ne rekusive Funktion zu inlinen?



  • T@@SSy schrieb:

    1. warum verwendet man nicht immer inline funktionen?

    1. das compilieren dauert länger, da immer alles neu compiliert werden muss.
    2. alle exe dateien werden extrem fett.
    3. der sinn von shared libraries, dass man sie austauschen kann, ohne die exe neu linken/compilieren zu müssen, wäre damit vernichtet. das macht zb bugfixes sehr kompliziert.
    4. sicher viele andere gründe, die mir nicht mehr einfallen 🙂

    T@@SSy schrieb:

    2. ich bin der meinung man sollte bei ganzzahlen werte wirklich auch den daten typ für die entsprechende grösse nehmen. also eine zahl 231 gehört eigentlich zu einem short int. warum aber sieht man meistens nur int?

    1. es wäre wohl unsigned char, da 231 unter 255 liegt. das ist auch gleich ein argument dagegen: es ist nicht sehr einfach, die richtige größe zu finden. vor allem wenn man architektur-unabhängig programmieren will, ist das nicht einfach.
    2. die meisten architekturen haben bestimmte "gute" und "schlechte" adressen bzw datentypgrößen. gut und schlecht sind in dem fall bezogen auf die performance. int bzw long int erzeugen im allgemeinen code, der schneller ausgeführt werden kann, da der prozessor dann mit dem rechnet, was er am besten kann.

    T@@SSy schrieb:

    3. wenn in einem if/else block fast identischer code vorkommt, lagere ich den identischen code in eine neue funktion aus, weil es meiner meinung nach schwachsinn ist, identische codezeilen in einem if/else block zu schreiben. selbst dann wenn es sich jeweils im nur 3-5 zeilen handelt. was denkt ihr darüber? gibt es eine bessere lösung?

    das kann man pauschal nicht sagen. es muss nicht unbedingt eine funktion sein. manchmal kann man die bedingungen so umformen, dass der code einfach vorher oder danach ausgeführt wird.

    T@@SSy schrieb:

    4. sollte man vor dem setzen eines wertes in einer if bedingung erst überprüfen ob es notwendig ist? oder macht das nichts aus?

    das setzen eines wertes braucht in den meisten fällen gleich lang wie die prüfung. man spart sich nur in sonderfällen zeit, die dann meistens nur ein paar mikrosekunden ist. die richtigen algorithmen für seinen code zu verwenden ist weitaus wichtiger.

    T@@SSy schrieb:

    5. gibt es einen speziellen syntax um folgendes beispiel besser zu schreiben, so dass man var2 nicht erneut schreiben muss?

    var1 = (var2 > 0) ? var2 : "";
    

    das beispiel verstehe ich nicht. var2 scheint eine integer variable zu sein, "" ist aber ein string. var1 hat demnach je nach bedingung verscheidene datentypen.



  • wow, mit sovielen beiträgen hätte ich nicht gerechnet. vielen dank euch allen. ich muss sagen jetzt macht es für mich sinn was inline funktionen angeht. ausschlaggebend war, das der code immer wieder an die stellen kopiert wird. darauf hätte ich auch selber kommen können 🙄 😉

    beim :? beispiel ist mir lediglich ein fehler unterlaufen 🤡. und was die zuweisung eines wertes ohne vorherige prüfung angeht, weiss ich nun auch wie ich es handhaben werde. ich weisse den wert immer ohne prüfung zu wenn es keine rolle spielt das der wert "überschrieben" werden soll. bislag war ich mir eben nicht sicher ob es eine performance einbusse gibt, wenn ich das so mache. aber nach einer kleinen überlegung wäre das ganze ja inetwa so:

    mit:

    ;(ungefär)
    cmp eax, esi
    mov esi, 120
    

    ohne:

    ;(ungefär)
    mov esi, 120
    

    also dann danke ich euch. dies hat mir eigentlich sehr geholfen. was die datentypen angeht werde ich schauen das ich keine relevanten fehler dabei mache.



  • noch schnell was zu:
    [quote=this->that]
    Kann man so generell nicht beantworten; kommt auf den konkreten Fall an. Wenn es Sinn macht eine Funktion daraus zu machen: klar. Aber wenn die Funktion nur an 2 Stellen aufgerufen wird und nur existiert um ein paar Zeilen Redunanz zu sparen und dann vermutlich auch noch einen recht nichtssagenden Namen hat, würde ich es eher lassen.
    [/quote]

    natürlich gehen wir davon aus das der if/else block nicht angepasst werden kann. es ist also garnicht mal so schlimm wenn gleicher code (mit ein paar abänderungen natürlich) erneut steht? ich finde das eigentlich schlechten stil und macht am ende die anwendung auch unnötig grösser. ein beispiel:

    for(int i = 0; i < Downloads.Length; i++)
    	{
    		if(Downloads[i]->filename != "")
    		{
    			if(FileExists(filename))
    			{
    				String hash = frmMain->GetHash(filename, true);
    				if(hash != Downloads[i]->hash)
    				{
    					DeleteFile(Downloads[i]->filename);
    					//ab hier....
    					TFileStream *pFile = new TFileStream(Downloads[i]->filename, fmCreate);
    					try{ IdHTTP->Get("http://www.web.com/" + Downloads[i]->filename, pFile); }
    					catch(...){}
    					delete pFile;
    				}
    			}
    			else
    			{
    				//ab hier....
    				TFileStream *pFile = new TFileStream(Downloads[i]->filename, fmCreate);
    				try{ IdHTTP->Get("http://www.web.com/" + Downloads[i]->filename, pFile); }
    				catch(...){}
    				delete pFile;
    			}
    		}
    	}
    

    ist jetzt nicht das perfekte beispiel dafür aber gehen wir davon aus, es gäbe noch mehr identische code zeilen. nun denke ich man könnte folgendes ändern:

    1. man benutzt boolische werte und benutzt den code block nur einmal am ende. dort wird auch mit einem boolischen wert überprüft ob es notwendig ist, DeleteFile auszuführen.

    2. man lagert den mini code in eine funktion aus, wie z.B. DownloadFile(std::string filename);

    3. man belässt es einfach so.

    wiegesagt, dass ist jetzt nicht das beste situations beispiel. es gab momenten wo ich wirklich lange überlegt habe wie man den block am besten schreiben könnte, damit nicht unnötiger code entsteht und ggf. auch keine auslagerung. irgendwelches feedback dazu oder tipps?



  • also gemeitn war 1. und 2. so:

    void DownloadFile(std::string filename)
    	{
    		TFileStream *pFile = new TFileStream(Downloads[i]->filename, fmCreate); 
            try{ IdHTTP->Get("http://www.web.com/" + filename, pFile); } 
            catch(...){} 
            delete pFile; 
    	}
    
    	for(int i = 0; i < Downloads.Length; i++) 
        { 
            if(Downloads[i]->filename != "") 
            { 
                if(FileExists(filename)) 
                { 
                    String hash = frmMain->GetHash(filename, true); 
                    if(hash != Downloads[i]->hash) 
                    { 
                        DeleteFile(Downloads[i]->filename); 
                        DownloadFile(Downloads[i]->filename); 
                    } 
                } 
                else 
                { 
                    DownloadFile(Downloads[i]->filename);
                } 
            } 
        }
    
    for(int i = 0; i < Downloads.Length; i++) 
        { 
            bool download = true;
    		if(Downloads[i]->filename != "") 
            { 
                if(FileExists(filename)) 
                { 
                    String hash = frmMain->GetHash(filename, true); 
                    if(hash != Downloads[i]->hash) DeleteFile(Downloads[i]->filename);  
    				else download = false;
                }
    			if(download)
    			{
                    TFileStream *pFile = new TFileStream(Downloads[i]->filename, fmCreate); 
                    try{ IdHTTP->Get("http://www.web.com/" + Downloads[i]->filename, pFile); } 
                    catch(...){} 
                    delete pFile;
    			}
            } 
        }
    

    wie findet ihr die zweite lösung dazu? prinzipiel würde die ja auch gut gehen. das liegt jetzt aber daran weil das beispiel echt nicht das beste war 🤡



  • T@@SSy schrieb:

    also gemeitn war 1. und 2. so:

    void DownloadFile(std::string filename)
    	{
    		TFileStream *pFile = new TFileStream(Downloads[i]->filename, fmCreate); 
            try{ IdHTTP->Get("http://www.web.com/" + filename, pFile); } 
            catch(...){} 
            delete pFile; 
    	}
    
    	for(int i = 0; i < Downloads.Length; i++) 
        { 
            if(Downloads[i]->filename != "") 
            { 
                if(FileExists(filename)) 
                { 
                    String hash = frmMain->GetHash(filename, true); 
                    if(hash != Downloads[i]->hash) 
                    { 
                        DeleteFile(Downloads[i]->filename); 
                        DownloadFile(Downloads[i]->filename); 
                    } 
                } 
                else 
                { 
                    DownloadFile(Downloads[i]->filename);
                } 
            } 
        }
    
    for(int i = 0; i < Downloads.Length; i++) 
        { 
            bool download = true;
    		if(Downloads[i]->filename != "") 
            { 
                if(FileExists(filename)) 
                { 
                    String hash = frmMain->GetHash(filename, true); 
                    if(hash != Downloads[i]->hash) DeleteFile(Downloads[i]->filename);  
    				else download = false;
                }
    			if(download)
    			{
                    TFileStream *pFile = new TFileStream(Downloads[i]->filename, fmCreate); 
                    try{ IdHTTP->Get("http://www.web.com/" + Downloads[i]->filename, pFile); } 
                    catch(...){} 
                    delete pFile;
    			}
            } 
        }
    

    wie findet ihr die zweite lösung dazu? prinzipiel würde die ja auch gut gehen. das liegt jetzt aber daran weil das beispiel echt nicht das beste war 🤡

    Der erste Code war für mich sofort verständlich.
    Den zweiten Code finde ich im Vergleich viel schwieriger zu lesen.

    Ich würde beide Lösungen kombinieren:

    void DownloadFile(std::string filename)
        {
            TFileStream *pFile = new TFileStream(Downloads[i]->filename, fmCreate);
            try{ IdHTTP->Get("http://www.web.com/" + filename, pFile); }
            catch(...){}
            delete pFile;
        }
    
        for(int i = 0; i < Downloads.Length; i++)
        {
            if(Downloads[i]->filename == "")
            {
             continue;
             }
    
                if(!FileExists(filename) || frmMain->GetHash(filename, true) != Downloads[i]->hash)
                { // Annahme: Datei braucht nicht gelöscht werden, da sie automatisch überschrieben wird bzw. auch automatisch erstellt wird.
                    DownloadFile(Downloads[i]->filename);
                }
        }
    


  • if(Downloads[i]->filename == "") 
    //=>
    if(Downloads[i]->filename.empty())
    

    weiß nicht, was übersichtlicher ist - aber schneller sollte empty auf jeden fall sein ^^ bei der anderen variante wird der string ja vrmtl mit nem const char* verglichen - evtl optimiert das der compiler aber weg - kann ich nicht sagen...
    ich nehm auf jeden fall immer std::string::empty()

    bb



  • unskilled schrieb:

    ich nehm auf jeden fall immer std::string::empty()

    Ich eigentlich auch. Meiner Ansicht nach sieht man da auch schneller, was geprüft wird... 😉

    Es kann aber auch Kontexte geben, in denen Prüfung auf "" meiner Ansicht nach sinnvoller ist. Beispielsweise, wenn ich eine Fallunterscheidung mache und auf mehrere Literale prüfe, ich der Konsistenz halber auch ein leeres Literal hinschreibe.


Anmelden zum Antworten