2 allgemeine Fragen zu C++



  • Frage Nr. 1:
    Dass Compiler eine gewisse Freiheit haben in bestimmten Situationen (zB. bei "undefiniertem" Verhalten) war mir klar. Aber in wie fern müssen Compiler "gleich funktionieren"?

    Ich habe folgendes Programm erstellt:

    #include <conio.h>
    #include <iostream>
    #include <windows.h>
    #include <vector>
    using namespace std;
    
    int main()
    {
    	DWORD tick = GetTickCount();
    
    		vector<unsigned short> ivec;
    		vector<unsigned short>::size_type n = 10000000;
    
    		for(vector<unsigned short>::size_type z = 0; z != n;++z) //vektor mit 10 Mio. Werten erstellen
    		{
    			ivec.push_back(5);
    		}
    
    		vector<unsigned short>::iterator t = ivec.begin();    // iteratoren vor der schleife initialisieren
    		vector<unsigned short>::iterator g = ivec.end();      // damit nicht bei jedem Schleifendruchlauf
    		vector<unsigned short>::iterator u = t;               // begin() und end() aufgerufen werden müssen
    
    		for( ; u != g;++u)
    		{
    			*u=4; //Werte im Vector von 5 auf 4 ändern
    		}
    
    	cout<<GetTickCount()-tick;
    	getch();
    }
    

    Grob gesagt erstellt es einen Vektor mit 10 Millionen Elementen und ändert diese dann. Zum Schluss gibt es die Zeit aus, die dafür benötigt wurde.

    Der Microsoft Visual C++ 6.0 gibt dabei 3641 (also ca 3.6sek), Code::Blocks v1.0 GNU GCC Compiler mingw32 312 aus.

    Woran liegt es, dass Codeblocks um den Faktor 10 schneller ist als der Microsoft Compiler?
    Haben Compiler wirklich eine so große Freiheit, den Quellcode in Maschinencode zu übersetzen?

    Frage Nr. 2:
    Ich wollte wissen, wie die funktion long double sqrt ( long double x ) aus <cmath> bzw <math.h> funktioniert.
    Kann ich irgendwo nachschauen wie diese Funktion definiert ist (in den Header-Dateien steht ja nur die deklaration), oder ist dieser Teil des Compilers hardcoded?
    Ist diese Funktion bei allen Compilern gleich implementiert oder kann es sein, dass ein Compiler einen anderen Algorithmus verwendet als ein anderer Compiler?



  • Erst einmal etwas grundsätzliches.

    for(<1>;<2>;<3>)
    1: Wird einmalig bei Beginn ausgeführt
    2: Wird jeweils am Anfang des Schleifendurchlaufes geprüft
    3: Wird jeweils am Ende des Schleifendurchlaufes ausgeführt

    Daher kannst du den Beginn-/Endeiterator durchaus in die for-Schleife verlagern:

    for(vector<unsigned short>::iterator it = ivec.begin(), ende = ivec.end(); // <1>
      it!=ende; // <2>
      ++it)
    //...
    

    TheCount schrieb:

    Grob gesagt erstellt es einen Vektor mit 10 Millionen Elementen und ändert diese dann. Zum Schluss gibt es die Zeit aus, die dafür benötigt wurde.

    Der Microsoft Visual C++ 6.0 gibt dabei 3641 (also ca 3.6sek), Code::Blocks v1.0 GNU GCC Compiler mingw32 312 aus.

    Mach mal ein:

    #define _SECURE_SCL 0
    // Edit: Korrigiert
    

    am Anfang und vergleiche dann die Werte (hoffe ich irre mich gerade nicht mit dem Namen dieses VC-Makros)...

    TheCount schrieb:

    Woran liegt es, dass Codeblocks um den Faktor 10 schneller ist als der Microsoft Compiler?

    Unterschiedliche STL-Implementierungen, zudem ist der VC-Compiler "überforsichtig". An sich habe ich nichts dagegen, wenn er das Verhalten abhängig ob im Debug- oder Releasemodus wechseln würde.

    TheCount schrieb:

    Haben Compiler wirklich eine so große Freiheit, den Quellcode in Maschinencode zu übersetzen?

    Der C++ Standard legt nur Rahmenbedingungen, keine konkrete Implementation fest.

    TheCount schrieb:

    Frage Nr. 2:
    Ich wollte wissen, wie die funktion long double sqrt ( long double x ) aus <cmath> bzw <math.h> funktioniert.
    Kann ich irgendwo nachschauen wie diese Funktion definiert ist (in den Header-Dateien steht ja nur die deklaration), oder ist dieser Teil des Compilers hardcoded?

    Nicht hardcoded aber meistens als Bibliothek vorhanden (und damit nicht zugreifbar).

    TheCount schrieb:

    Ist diese Funktion bei allen Compilern gleich implementiert oder kann es sein, dass ein Compiler einen anderen Algorithmus verwendet als ein anderer Compiler?

    Unterschiedliche Implementierung ist wie gesagt erlaubt, sofern die Aufrufgarantien des Standards gewährleistet sind.

    cu André



  • TheCount schrieb:

    Der Microsoft Visual C++ 6.0 gibt dabei 3641 (also ca 3.6sek), Code::Blocks v1.0 GNU GCC Compiler mingw32 312 aus.

    Woran liegt es, dass Codeblocks um den Faktor 10 schneller ist als der Microsoft Compiler?
    Haben Compiler wirklich eine so große Freiheit, den Quellcode in Maschinencode zu übersetzen?

    Ja, es gibt überhaupt keine Vorschrift wie der Maschinencode aussehen muss, die Compilerhersteller versuchen den Maschinencode so zu optimieren, dass das was im C++ Code steht so schnell wie möglich abgearbeitet wird. Der Faktor 10 bei dir wird aber wahrscheinlich daran liegen, dass du Debugcode generierst.



  • uberpro schrieb:

    Der Faktor 10 bei dir wird aber wahrscheinlich daran liegen, dass du Debugcode generierst.

    + MS-Spezifische Zusatzprüfungen, siehe _SECURE_SCL (Ich korrigiere es auch noch oben...)
    Edit: Ups... VC6, zugegeben Überlesen... Da gilt die Aussage eh nicht



  • Wobei bei der VC++ 6 vielleicht noch nicht so gut optimieren kann. Ausserdem ist er sowieso nicht gerade empfehlenswert.

    Nutz doch die Visual C++ 2008 Express Edition, die ist kostenlos und kann auch um einiges mehr.



  • asc schrieb:

    uberpro schrieb:

    Der Faktor 10 bei dir wird aber wahrscheinlich daran liegen, dass du Debugcode generierst.

    + MS-Spezifische Zusatzprüfungen, siehe _SECURE_SCL (Ich korrigiere es auch noch oben...)
    Edit: Ups... VC6, zugegeben Überlesen... Da gilt die Aussage eh nicht

    ich bin doch uberpro, ich weiß schon was ich schreib :p , der zusätzlich check macht doch keinen faktor 10.

    So jetzt hab ich mir auch noch den Code angeschaut und was da noch sein könnte, ist das die 10M push_backs den unterschied machen, weil nicht vorgeschrieben ist um wieviel der vector vergrößert wird, wenn der Platz nicht reicht und da könnte GCC ne variante haben die besser zu deinem fall passt.



  • Dann versuch doch mal, durch std::vector::reserve() etwas Ausgleich zu schaffen.



  • Hallo,

    ich habe den Code von "asmcode" verwendet, um den Performancetest zwischen Vektoren und Arrays nachzuvollziehen. Zusätzlich habe ich noch den von "Shadow Of Mine" empfohlen Schalter gesetzt. Zum Kompilieren habe ich den Defaultcompiler von MSV 2005 verwendet.

    Leider bringt der Schalter keine messbare Performanceverbesserung. Hat jemand eine Erklärung?

    Viele Grüße!

    Test Programm:

    #include <vector>
    #include <iostream>
    #include <windows.h>
    
    void foo()
    {
    
    	LONGLONG g_Frequency, g_CurentCount, g_LastCount;
    
        //Frequenz holen
        if (!QueryPerformanceFrequency((LARGE_INTEGER*)&g_Frequency))
            std::cout << "Performance Counter nicht vorhanden" << std::endl;
    
        //1. Messung
        QueryPerformanceCounter((LARGE_INTEGER*)&g_CurentCount);
    
        int max=50000;
        std::vector<int> a(max);
        for (int i=0; i<=max-2; i++)
        {
            int x,Min = i;
            for (int j=i+1; j<=max-1; j++)
                if (a[j]<a[Min]) Min = j;
            x = a[i]; a[i] = a[Min]; a[Min] = x;
        }
    
        //2. Messung
        QueryPerformanceCounter((LARGE_INTEGER*)&g_LastCount);
    
        double dTimeDiff = (((double)(g_LastCount-g_CurentCount))/((double)g_Frequency));
        std::cout << "Zeit: " << dTimeDiff << std::endl;
    }
    
    void bar()
    {
        LONGLONG g_Frequency, g_CurentCount, g_LastCount;
    
        //Frequenz holen
        if (!QueryPerformanceFrequency((LARGE_INTEGER*)&g_Frequency))
            std::cout << "Performance Counter nicht vorhanden" << std::endl;
    
        //1. Messung
        QueryPerformanceCounter((LARGE_INTEGER*)&g_CurentCount);
    
        int max=50000;
        int* a = new int[max];
        for (int i=0; i<=max-2; i++)
        {
            int x,Min = i;
            for (int j=i+1; j<=max-1; j++)
                if (a[j]<a[Min]) Min = j;
            x = a[i]; a[i] = a[Min]; a[Min] = x;
        }
    
        //2. Messung
        QueryPerformanceCounter((LARGE_INTEGER*)&g_LastCount);
    
        double dTimeDiff = (((double)(g_LastCount-g_CurentCount))/((double)g_Frequency));
        std::cout << "Zeit: " << dTimeDiff << std::endl;
    }
    
    int main(int argc, char**argv)
    {
    
        #ifdef _SECURE_SCL
        std::cout << " _SECURE_SCL Value: " << _SECURE_SCL <<std::endl;
        #endif
        foo();
        bar();
    
        #define _SECURE_SCL 0
    
        #ifdef _SECURE_SCL
        std::cout << " _SECURE_SCL Value: " << _SECURE_SCL <<std::endl;
        #endif
        foo();
        bar();
    
    	getchar();
    }
    

    Ausgabe

    _SECURE_SCL Value: 1
    Zeit: 8.40913
    Zeit: 1.83019
    _SECURE_SCL Value: 0
    Zeit: 8.3748
    Zeit: 1.82929
    


  • Cool_Fire schrieb:

    Leider bringt der Schalter keine messbare Performanceverbesserung. Hat jemand eine Erklärung?

    int main(int argc, char**argv)
    {
        
        #ifdef _SECURE_SCL
        std::cout << " _SECURE_SCL Value: " << _SECURE_SCL <<std::endl;
        #endif
        foo();
        bar();
    
        #define _SECURE_SCL 0
    	
        #ifdef _SECURE_SCL
        std::cout << " _SECURE_SCL Value: " << _SECURE_SCL <<std::endl;
        #endif
        foo();
        bar();
    
    	getchar();
    }
    

    Klar, weil Macros so nicht funktionieren. Macros werden vom Pre-Prozessor verarbeitet, also noch vor dem Compiler und gelten ab dem Moment wo sie definiert werden bis zum Ende der "Übersetzungseinheit" oder bis zum undef.

    Auf die Funktionen foo() und (bar() in deinem Beispiel haben die also gar keinen Einfluß weil die ja oberhalb der defines im Sourcecode stehen.


Log in to reply