VC++ 2005 vs. Mingw 3.4.5



  • Hi,

    ich schreib schon seit über 2 Jahren immer wieder ein Programm, das Primzahlen in einem bestimmten Bereich sucht und verbessere dieses Programm immer weiter.

    Jetzt möchte ich gerne auf SSE optimieren.

    folgenden Code hab ich produziert.

    def.h

    #ifndef DEFH
    #define DEFH
    #include <cmath>
    
    typedef unsigned long uint; // neuer Typ um einfach den Typ im ganzen Programm ändern zu können
    
    const uint lim = 100000000; // Such grenze
    const uint sqrtlim = sqrt(static_cast<double>(lim));
    
    #endif
    

    BoolArray.h

    #ifndef BOOLARRAY_H
    #define BOOLARRAY_H
    #include "def.h"
    const uint divi = 32;
    const uint dataCount = lim/(divi*2);
    class BoolArray
    {
        public:
            BoolArray();
            virtual ~BoolArray();
            BoolArray(const BoolArray& op);
            BoolArray& operator =(const BoolArray& op);
            void setBit(const uint& addr); // DO NOT USE
            void clrBit(uint addr);
            bool getBit(uint addr) const;
            uint getNumber() const;
        protected:
        private:
        uint* m_data;
    };
    
    #endif // BOOLARRAY_H
    

    BoolArray.cpp

    #include "BoolArray.h"
    
    BoolArray::BoolArray()
    {
        //ctor
        m_data = new uint[dataCount];
        for(uint i=0;i<dataCount;++i)
        {
            m_data[i]=0xffffffff;
        }
    }
    
    BoolArray::~BoolArray()
    {
        //dtor
        delete[] m_data;
    }
    /* nicht nötig deshalb leer */
    void BoolArray::setBit(const uint& addr)
    {
    
    }
    
    void BoolArray::clrBit(uint addr)
    {
     --addr;
     addr/=2;
     switch(addr%32)
     {
        case 0x0: m_data[addr/divi] &= 0xfffffffe; break; // E
        case 0x1: m_data[addr/divi] &= 0xfffffffd; break; // D
        case 0x2: m_data[addr/divi] &= 0xfffffffb; break; // B
        case 0x3: m_data[addr/divi] &= 0xfffffff7; break; // 7
        case 0x4: m_data[addr/divi] &= 0xffffffef; break;
        case 0x5: m_data[addr/divi] &= 0xffffffdf; break;
        case 0x6: m_data[addr/divi] &= 0xffffffbf; break;
        case 0x7: m_data[addr/divi] &= 0xffffff7f; break;
        case 0x8: m_data[addr/divi] &= 0xfffffeff; break;
        case 0x9: m_data[addr/divi] &= 0xfffffdff; break;
        case 0xA: m_data[addr/divi] &= 0xfffffbff; break;
        case 0xB: m_data[addr/divi] &= 0xfffff7ff; break;
        case 0xc: m_data[addr/divi] &= 0xffffefff; break;
        case 0xd: m_data[addr/divi] &= 0xffffdfff; break;
        case 0xe: m_data[addr/divi] &= 0xffffbfff; break;
        case 0xf: m_data[addr/divi] &= 0xffff7fff; break;
        case 0x10:m_data[addr/divi] &= 0xfffeffff; break;
        case 0x11:m_data[addr/divi] &= 0xfffdffff; break;
        case 0x12:m_data[addr/divi] &= 0xfffbffff; break;
        case 0x13:m_data[addr/divi] &= 0xfff7ffff; break;
        case 0x14:m_data[addr/divi] &= 0xffefffff; break;
        case 0x15:m_data[addr/divi] &= 0xffdfffff; break;
        case 0x16:m_data[addr/divi] &= 0xffbfffff; break;
        case 0x17:m_data[addr/divi] &= 0xff7fffff; break;
        case 0x18:m_data[addr/divi] &= 0xfeffffff; break;
        case 0x19:m_data[addr/divi] &= 0xfdffffff; break;
        case 0x1a:m_data[addr/divi] &= 0xfbffffff; break;
        case 0x1b:m_data[addr/divi] &= 0xf7ffffff; break;
        case 0x1c:m_data[addr/divi] &= 0xefffffff; break;
        case 0x1d:m_data[addr/divi] &= 0xdfffffff; break;
        case 0x1e:m_data[addr/divi] &= 0xbfffffff; break;
        case 0x1f:m_data[addr/divi] &= 0x7fffffff; break;
     }
    }
    
    bool BoolArray::getBit(uint addr) const
    {
     --addr;
     addr/=2;
     switch(addr%32)
     {
        case 0x0: if(m_data[addr/divi] & 0x00000001)return true; return false; break; // 1
        case 0x1: if(m_data[addr/divi] & 0x00000002)return true; return false; break; // 2
        case 0x2: if(m_data[addr/divi] & 0x00000004)return true; return false; break; // 4
        case 0x3: if(m_data[addr/divi] & 0x00000008)return true; return false; break; // 8
        case 0x4: if(m_data[addr/divi] & 0x00000010)return true; return false; break;
        case 0x5: if(m_data[addr/divi] & 0x00000020)return true; return false; break;
        case 0x6: if(m_data[addr/divi] & 0x00000040)return true; return false; break;
        case 0x7: if(m_data[addr/divi] & 0x00000080)return true; return false; break;
        case 0x8: if(m_data[addr/divi] & 0x00000100)return true; return false; break;
        case 0x9: if(m_data[addr/divi] & 0x00000200)return true; return false; break;
        case 0xA: if(m_data[addr/divi] & 0x00000400)return true; return false; break;
        case 0xB: if(m_data[addr/divi] & 0x00000800)return true; return false; break;
        case 0xc: if(m_data[addr/divi] & 0x00001000)return true; return false; break;
        case 0xd: if(m_data[addr/divi] & 0x00002000)return true; return false; break;
        case 0xe: if(m_data[addr/divi] & 0x00004000)return true; return false; break;
        case 0xf: if(m_data[addr/divi] & 0x00008000)return true; return false; break;
        case 0x10:if(m_data[addr/divi] & 0x00010000)return true; return false; break;
        case 0x11:if(m_data[addr/divi] & 0x00020000)return true; return false; break;
        case 0x12:if(m_data[addr/divi] & 0x00040000)return true; return false; break;
        case 0x13:if(m_data[addr/divi] & 0x00080000)return true; return false; break;
        case 0x14:if(m_data[addr/divi] & 0x00100000)return true; return false; break;
        case 0x15:if(m_data[addr/divi] & 0x00200000)return true; return false; break;
        case 0x16:if(m_data[addr/divi] & 0x00400000)return true; return false; break;
        case 0x17:if(m_data[addr/divi] & 0x00800000)return true; return false; break;
        case 0x18:if(m_data[addr/divi] & 0x01000000)return true; return false; break;
        case 0x19:if(m_data[addr/divi] & 0x02000000)return true; return false; break;
        case 0x1a:if(m_data[addr/divi] & 0x04000000)return true; return false; break;
        case 0x1b:if(m_data[addr/divi] & 0x08000000)return true; return false; break;
        case 0x1c:if(m_data[addr/divi] & 0x10000000)return true; return false; break;
        case 0x1d:if(m_data[addr/divi] & 0x20000000)return true; return false; break;
        case 0x1e:if(m_data[addr/divi] & 0x40000000)return true; return false; break;
        case 0x1f:if(m_data[addr/divi] & 0x80000000)return true; return false; break;
     }
    }
    
    BoolArray::BoolArray(const BoolArray& op)
    {
     m_data = new uint[dataCount];
     for(uint i=0;i<dataCount;++i)
     {
        m_data[i]=op.m_data[i];
     }
    }
    
    BoolArray& BoolArray::operator =(const BoolArray& op)
    {
     delete[] m_data;
     m_data = new uint[dataCount];
     for(uint i=0;i<dataCount;++i)
     {
        m_data[i]=op.m_data[i];
     }
     return *this;
    }
    
    uint BoolArray::getNumber() const
    {
     uint erg=1;
     for(uint i=3;i<lim;i+=2) if(this->getBit(i)) erg++;
     return erg;
    }
    

    main.cpp

    #include <iostream>
    #include <ctime>
    #include "def.h"
    #include "BoolArray.h"
    
    using namespace std;
    /*
    void cdecl calc(void* pParam)
    {
    BoolArray* Primes = static_cast<BoolArray*>(pParam);
    
    return;
    }*/
    
    int main()
    {
        BoolArray Primes;
    //  _beginthread(calc,1000,&Primes);
        uint b[8],primeCount=1,p=0;
        uint prim[8],mult[8];
        bool c[8];
       /* Primes.clrBit(9);
        Primes.clrBit(15);
        Primes.clrBit(21); */
        double t1,t2;
        t1 = -clock();
        for(uint i=3;i<=sqrtlim;i+=2)
        {
            if(Primes.getBit(i))
            {
                prim[p]=i; // new
                ++p;
                if(p==2)
                {
                    b[0]=lim/prim[0];
                    b[1]=lim/prim[1];
                  /*  b[2]=lim/prim[2];
                    b[3]=lim/prim[3];
                    b[4]=lim/prim[4];
                    b[5]=lim/prim[5];
                    b[6]=lim/prim[6];
                    b[7]=lim/prim[7]; */
                    mult[0]=prim[0];
                    mult[1]=prim[1];
                 /*   mult[2]=prim[2];
                    mult[3]=prim[3];
                    mult[4]=prim[4];
                    mult[5]=prim[5];
                    mult[6]=prim[6];
                    mult[7]=prim[7]; */
                    while(1)
                        {
                            if(c[0] && c[1] /* && c[2] && c[3] && c[4] && c[5] && c[6] && c[7]*/)
                            {
                                c[0]=false;
                                c[1]=false;
                             /*   c[2]=false;
                                c[3]=false;
                                c[4]=false;
                                c[5]=false;
                                c[6]=false;
                                c[7]=false; */
                                break;
                            }
                            if(mult[0] <= b[0])
                            {
                                Primes.clrBit(prim[0]*mult[0]);
                                mult[0]+=2;
                            }
                            else {c[0] =true; continue;
                            }
                            if(mult[1] <= b[1])
                            {
                                Primes.clrBit(prim[1]*mult[1]);
                                mult[1]+=2;
                            }
                            else {c[1] =true; continue;
                            }
                         /*   if(mult[2] <= b[2])
                            {
                                Primes.clrBit(prim[2]*mult[2]);
                                mult[2]+=2;
                            }
                            else {c[2] =true; continue;
                            }
                            if(mult[3] <= b[3])
                            {
                                Primes.clrBit(prim[3]*mult[3]);
                                mult[3]+=2;
                            }
                            else {c[3] =true; continue;
                            }
                            if(mult[4] <= b[4])
                            {
                                Primes.clrBit(prim[4]*mult[4]);
                                mult[4]+=2;
                            }
                            else {c[4] =true; continue;
                            }
                            if(mult[5] <= b[5])
                            {
                                Primes.clrBit(prim[5]*mult[5]);
                                mult[5]+=2;
                            }
                            else {c[5] =true; continue;
                            }
                            if(mult[6] <= b[6])
                            {
                                Primes.clrBit(prim[6]*mult[6]);
                                mult[6]+=2;
                            }
                            else {c[6] =true; continue;
                            }
                            if(mult[7] <= b[7])
                            {
                                Primes.clrBit(prim[7]*mult[7]);
                                mult[7]+=2;
                            }
                            else {c[7] =true; continue;
                            } */
    
                        }
                    p=0;
                }
            }
        }
        t2 = clock();
        primeCount = Primes.getNumber();
    
        cout<<primeCount<<endl<<(t1+t2)/CLOCKS_PER_SEC;
        getchar();
        return 0;
    }
    

    Mit dem Auskommentieren, in der main.cpp hab ich nur den Grad der vektorisierung geändert

    so erstmal ist mir dabei etwas interessantes aufgefallen, während g++ ein korrekt funktionierendes Programm produziert, tut das der MS Compiler nicht.
    Die Ausgabe müsste 5761455 lauten.

    so 1. Frage:
    Wiso kann der MS Compiler kein korrektes Programm erzeugen der g++ aber schon?
    2. gibt es bereits kostenlose C++ Compiler, die auf SSE optimieren können? Und wird mir das etwas bringen?



  • walljumper schrieb:

    so 1. Frage:
    Wiso kann der MS Compiler kein korrektes Programm erzeugen der g++ aber schon?

    Weil dein Programm undefiniertes Verhalten hat - da ist alles erlaubt 😉
    du solltest c initialisieren... - das ist übrigens etwas, worüber dich beide Compiler informieren sollten. Im Prinzip ein Problem, das es gar nicht geben dürfte - aber der Kampf gegen Definionen ohne Initialisierung ist offensichtlich keiner, der gewonnen werden kann:

    uint b[8],primeCount=1,p=0;
        uint prim[8],mult[8];
        bool c[8];
    

    das ist einfach Mist.

    Eine gute Einstellung für Warnungen bei vc++:
    /W4 /Wp64 /Wall
    und dann die nutzlosen und nervigen abschalten (Advanced/Disable Specific Warnings): 4514;4548;4530;4619;4668;4710;4711;4820



  • ok, da hast du natürlich recht, das kommt davon wenn man den Code ständig umschreibt, da verliert man irgendwann der Überblick.

    Also 1. problem gelöst.

    Die Performance ist jetzt minimal schlechter als ohne vektorisierung, kommt wohl durch den zusätzlichen Verwaltungsaufwand. Wie kann ich jetzt SSE Code erzeugen? Muss ich dazu inline Assembler verwenden?





  • Jochen Kalmbach schrieb:

    Ab VC2008 gibt es Compiler-Intrinsics für SSEx
    http://blogs.msdn.com/vcblog/archive/2007/10/18/new-intrinsic-support-in-visual-studio-2008.aspx

    VC 200[3|5] hatten doch schon Intrinsics, oder?

    2. gibt es bereits kostenlose C++ Compiler, die auf SSE optimieren können? Und wird mir das etwas bringen?

    Ja, natuerlich. Beim GCC/MingW besonders interessant in diesem Zusammenhang sind sicher die Versionen >= 4, die besitzen Auto-Vectorisation, d.h. sie koennen gewissen Schleifen automatisch in SSE-Varianten umschreiben. Ich koennt mir vorstellen, dass der VC2005 sowas auch hat.
    Aber kompilier dein Programm doch einfach mal mit dem MinGW 4.2 und schau, ob es merkbar schneller wird.

    Ansonsten musst du wohl mit Intrinsics o. AE. arbeiten. Dazu wuerdest du aber Teile deines Programmes umschreiben muessen. Sollte aber in beiden Compilern funktionieren.



  • VC 200[3|5] hatten doch schon Intrinsics, oder?

    Ja

    Mir wäre es aber lieber wenn der Compiler das alleine packt.

    Ja, natuerlich. Beim GCC/MingW besonders interessant in diesem Zusammenhang sind sicher die Versionen >= 4, die besitzen Auto-Vectorisation, d.h. sie koennen gewissen Schleifen automatisch in SSE-Varianten umschreiben. Ich koennt mir vorstellen, dass der VC2005 sowas auch hat.
    Aber kompilier dein Programm doch einfach mal mit dem MinGW 4.2 und schau, ob es merkbar schneller wird.

    Ich hab mal Mingw 4.2 runtergeladen aber beim kompilieren, tritt ein Fehler auf.

    C:\myProjects\Primer1.0\BoolArray.cpp:19: warning: unused parameter 'addr'
    ...)]+0x8e):: undefined reference to \_\_mingw\_vsnprintf' ...)]+0xdf):: undefined reference to__mingw_vsnprintf'
    :: === Build finished: 2 errors, 1 warnings ===

    Pfade sind eigentlich alle gesetzt.



  • Mach mal ein komplettes Rebuild aller Dateinen und versuch mal -lmingw zu den Linker-Optionen hinzuzufuegen, vielleicht hilft das 🙂

    EDIT: und solltest du den GCC 4.2 ueber deine alte MinGW-Installation drueber installiert haben, dann vergiss nicht, dass du auch neue Versionen der binutils, der mingw-runtime und der WinAPI installieren musst!



  • EDIT: und solltest du den GCC 4.2 ueber deine alte MinGW-Installation drueber installiert haben, dann vergiss nicht, dass du auch neue Versionen der binutils, der mingw-runtime und der WinAPI installieren musst!

    Jo das hat ich vergessen.

    Ich kanns jetzt compilieren, das problem ist nur, das die Performance nur noch knapp die hälfte beträgt.



  • walljumper schrieb:

    EDIT: und solltest du den GCC 4.2 ueber deine alte MinGW-Installation drueber installiert haben, dann vergiss nicht, dass du auch neue Versionen der binutils, der mingw-runtime und der WinAPI installieren musst!

    Jo das hat ich vergessen.

    Ich kanns jetzt compilieren, das problem ist nur, das die Performance nur noch knapp die hälfte beträgt.

    Wow, das ueberrascht mich. Mit welchen Compileroptionen denn?



  • mich hat es auch gewundert

    mit minGW 4.2.1 hat das Program eine laufzeit von ~4,7 sec

    mit minGw 3.4.5 ~2,3 sec

    die Optionen sind beidesmal die selben

    -O3
    -fexpensive-optimizations
    -march=nocona



  • Auch wenn es vermutlich nichts bringen wird, verwende mal statt -march=... -mtune=pentium-m. Zudem lass mal das -fexpensive-optimizations Flag weg. Ansonsten habe ich das Gefühl, dass Version 4 vom GCC noch nicht wirklich gut optimiert, speziell wenn es um SSE geht.



  • jop bringt wirklich nix.

    Ich hab jetzt folgende Zeiten

    VC++ 2005 : ~ 1,3 sec
    MinGw 3.4.5 : ~1,6 sec
    Min Gw 4.2.1: ~ 3,3 Sec

    Ich guck jetzt mal ob ich es mit den Intrinsics von VC++ beschleunigt bekomm.

    Kennt vieleicht jemand eine Übersicht über die SSE Befehle?



  • Was meinst du mit Übersicht? ZB die Dokus von AMD (26568) oder Intel (253666, 253667)?

    Ansonsten, die Intrinsics sollten auch in den entsprechenden Headern (xmmintrin.h, emmintrin.h, pmmintrin.h) ausreichend beschrieben sein.

    Btw, hast du mal GCC 4.2.2 probiert?



  • Was meinst du mit Übersicht? ZB die Dokus von AMD (26568) oder Intel (253666, 253667)?

    Ansonsten, die Intrinsics sollten auch in den entsprechenden Headern (xmmintrin.h, emmintrin.h, pmmintrin.h) ausreichend beschrieben sein.

    Hat sich erledigt, hab die Intel Dokus von nem Kumpel bekommen.

    Aber die Header alleine reichen nicht wirklich.

    Ich hab den Code jetzt so optimiert, dass 4 additionen aufeinmal erledigt werden, leider sinkt die Performance dadurch deutlich.
    Multiplikationen können leider nur 2 gleichzeitig erledigt werden.

    scheint sich also nicht zu lohnen.

    den gcc 4.2.2 probier ich aber mal aus.


Anmelden zum Antworten