Primzahlenberechnung - Sieb des Eratosthenes - Was kann noch optimiert werden?



  • man lässt glaube ich üblicherweise die geraden zahlen weg. dadurch kann man bei gleichem speicherverbrauch und ähnlicher laufzeit doppelt so viele zahlen sieben.



  • Das schaut doch ganz ordentlich aus.

    Ist mir so beim Überfliegen aufgefallen:

    In Zeile 22 schreibst du (k>=2); , aber in Zeile 27 geht es nur weiter, wenn if(k>=i).



  • volkard schrieb:

    i*k muss weg!!

    Stimmt, vb[i*k]=(true) ist doppelt drin, da ist ein Fehler bei Copy & Paste passiert.

    volkard schrieb:

    Du kannst auch wegpos-=k machen.

    Das verstehe ich leider nicht, was meinst du damit?

    volkard schrieb:

    Warum eigenlich ist die Schleife rückwärts? Ich kenne 92 Gründe für Vorwärts aber nur 18 Gründe für Rückwärts.

    Wenn die Schleife vorwärts läuft, wird z.B. bei i=2 und k=2,n=4 gestrichen. Im übernächsten Schritt wäre i=2;k=4, da n=4 aber schon gestrichen wurde, bliebe n=8 stehen.

    Läuft die Schleife rückwärts, hat man diese Problematik nicht.



  • Die geraden Zahlen lässt man tatsächlich aus. Aber das geht in vector<bool> wohl nicht.

    Aber du kannst in Zeile 18 mit i=3 anfangen und dann immer i+=2; weiter gehen. Am Ende auf gleiche Weise einfach nicht die geraden Zahlen ausgeben.



  • ichbineinnoob schrieb:

    Das schaut doch ganz ordentlich aus.

    Ist mir so beim Überfliegen aufgefallen:

    In Zeile 22 schreibst du (k>=2); , aber in Zeile 27 geht es nur weiter, wenn if(k>=i).

    Ja, stimmt, irgendwie unlogisch.
    Hab Zeile 22 gleich mal auf (k>=i) korrigiert.

    Nachtrag: Dementsprechend ist dann natürlich auch die if-Abfrage in Zeile 27
    überflüssig, auch gleich mit korrigiert.



  • Eigentlich müßte es doch auch möglich sein, die Ausgabe auf der Konsole anstelle einer separaten Schleife jeweils dann zu tätigen, wenn i zum nächsten Wert(z.B. i*i) springt bzw. gesprungen ist, da alle Zahlen dazwischen, die nicht gestrichen wurden, Primzahlen sein müssen.



  • redrew99 schrieb:

    Eigentlich müßte es doch auch möglich sein, die Ausgabe auf der Konsole anstelle einer separaten Schleife jeweils dann zu tätigen, wenn i zum nächsten Wert(z.B. i*i) springt bzw. gesprungen ist, da alle Zahlen dazwischen, die nicht gestrichen wurden, Primzahlen sein müssen.

    Ja, ab i abwärts ist die Liste "fertig"



  • ichbineinnoob schrieb:

    Die geraden Zahlen lässt man tatsächlich aus. Aber das geht in vector<bool> wohl nicht.

    Wieso sollte das mit vector<bool> nicht gehen? Einfach den Index halbieren...



  • vector<bool> ist eine Spezialisierung von vector, um pro Bool-Wert auch tatsächlich nur ein Bit zu verwenden. Theoretisch könnte ein vector<char> schneller sein. Praktisch muss man es nachmessen.



  • manni66 schrieb:

    Theoretisch könnte ein vector<char> schneller sein.

    Wegen was? Der Bitmaske?


  • Mod

    EOutOfResources schrieb:

    manni66 schrieb:

    Theoretisch könnte ein vector<char> schneller sein.

    Wegen was? Der Bitmaske?

    Weil vector<bool> eine unnötig komplizierte Spezialisierung ist, während ein vector<char> der übliche schnelle vector wäre.


  • Mod

    SeppJ schrieb:

    EOutOfResources schrieb:

    manni66 schrieb:

    Theoretisch könnte ein vector<char> schneller sein.

    Wegen was? Der Bitmaske?

    Weil vector<bool> eine unnötig komplizierte Spezialisierung ist, während ein vector<char> der übliche schnelle vector wäre.

    Was noch wichtig ist: Solche Überlegungen müssen nicht stimmen. Man kann zum Beispiel leicht vergessen, dass ein vector<bool> viel bessere Lokalität hat. Wenn der ganze vector (oder große Teile) in den CPU-Cache passen, dann ist das ein gewaltiger Vorteil. Ich hab's mal ausprobiert:

    #include <vector>
    #include <iostream>
    #include <cstddef>
    
    using namespace std;
    
    typedef vector<bool> container;  // Hier entsprechend auf char oder bool setzen
    
    int main()
    {
      const size_t max_n = 100000000;
      container numbers(max_n, 1);
      for (unsigned n=2; n<max_n; ++n)
        if (numbers[n])
          for (unsigned i=2*n; i<max_n; i+=n)
            numbers[i]=0;
      unsigned long anti_optimizer = 0;
      for (unsigned n=2; n<max_n; ++n)
        if (numbers[n])
          anti_optimizer += n;
      cout<<"Summe aller Primzahlen bis "<<max_n<<" ist "<<anti_optimizer<<".\n";
    }
    

    Die vector<char> Version rechnet bei mir (Core 2, g++, -O3 -xHOST) knapp 8 Sekunden, der vector<bool> etwas über 5 Sekunden. Also ganz klarer Sieg für den kleinen, aber komplizierten, Datentyp.



  • SeppJ schrieb:

    Wenn der ganze vector (oder große Teile) in den CPU-Cache passen, dann ist das ein gewaltiger Vorteil.
    ...
    Die vector<char> Version rechnet bei mir (Core 2, g++, -O3 -xHOST) knapp 8 Sekunden, der vector<bool> etwas über 5 Sekunden. Also ganz klarer Sieg für den kleinen, aber komplizierten, Datentyp.

    Jup. Der vector paßt wohl gerade rein? Rein interessehalber ie steht's bei max_n=1e9?



  • SeppJ schrieb:

    Die vector<char> Version rechnet bei mir (Core 2, g++, -O3 -xHOST) knapp 8 Sekunden, der vector<bool> etwas über 5 Sekunden. Also ganz klarer Sieg für den kleinen, aber komplizierten, Datentyp.

    ich weiß nicht mit welchem Code du getestet hast, aber ich hab's mal mit deinem probiert (max_n = 100000000;). Ergebnis:

    vector<char> container; -> 9s
    vector<bool> container; -> 19s

    Edit: hab die Angaben korrigiert.


  • Mod

    Auf meinem Rechner (Atom 330) mit n=100000000:
    char: 10.35s
    bool: 12.75s

    Die Lokalität mit bool ist nicht von besonders großem Vorteil, wenn man bedenkt, dass bool für jeden einzelnen Wert sowohl eine Lese- als auch eine Schreibvorgang impliziert. Wärend mit char nur geschrieben wird - hier könnte man ggf. auch low-level optimieren, z.B. mit unsigned arbeiten und ungeordnet schreiben.



  • [Rewind] schrieb:

    probiert (max_n = 1000000;). Ergebnis:
    vector<char> container; -> 2-3s
    vector<bool> container; -> 32-33s

    Naja, -O3 muß schon sein. Am besten auch -march=native.
    So lahm kann Dein Rechner gar nicht sein.

    Bei mir:
    Bis 1e5
    bool: 0.0014
    char: 0.0015

    Bis 1e6
    bool: 0.14
    char: 0.18

    Bis 1e7
    bool: 1.08
    char: 1.84

    Bis 1e8
    bool: 13.77
    char: 21.11

    Und der Prozessor ist nicht neu. AMD Sempron 3000+ @ 1800MhZ.


  • Mod

    [Rewind] schrieb:

    ich weiß nicht mit welchem Code du getestet hast, aber ich hab's mal mit deinem probiert (max_n = 1000000;). Ergebnis:

    vector<char> container; -> 2-3s
    vector<bool> container; -> 32-33s

    Das widerspricht sich ja auch nicht. Je nach Compiler und System können schon so große Unterschiede auftauchen. Anscheinend hat deine Standardbib einen sehr schlechten vector<bool>, meine einen sehr guten. Darf ich fragen, welcher Compiler?

    volkard schrieb:

    Jup. Der vector paßt wohl gerade rein? Rein interessehalber ie steht's bei max_n=1e9?

    Eigentlich nicht, der Prozessor müsste irgendwie so um 2-4MB Cache haben. Der vector müsste aber mindestens 12.5 MB groß sein.

    Abgesehen davon, dass ich gerade an einem anderen Rechner sitze, dürfte mein Core2 Rechner jedoch Probleme mit 1 Milliarde char bekommen, von der RAM-Größe her, daher unvergleichbar.

    Ich sitze gerade an einem i7 mit viel mehr RAM und vergleichbar großem Cache, da probiere ich mal (gleicher Compiler wie oben) mit 1 Milliarde:
    vector<bool>: gut 24 Sekunden
    vector<char>: knapp 26 Sekunden

    Sehr interessant. Ich lasse mal im Hintergrund mit 8 Milliarden laufen und melde mich in 10-20 Minuten mit den Ergebnissen.

    edit: Ach, dazu habe ich nicht die Geduld. Dafür die Ergebnisse von dem i7 Rechner mit 100 Millionen:
    bool: 1.75 Sekunden
    char: 2 Sekunden

    Da das auch ungefähr das gleiche ist wie bei 1 Milliarde, breche ich mal den 8 Milliarden test ab, da wird sicherlich nichts großarti anderes rauskommen.



  • Wie bereits in der letzten Antwort dazugeschrieben, die Zeitangaben habe ich korrigiert (9s für char und 19s für bool mit n=100mil). Ich verwende momentan VS2008 PE.


  • Mod

    Was mir gerade auffällt: Wenn man sich mal Zwischenergebnisse ausgeben lässt, sieht man, dass ein sehr großer Teil der Zeit für das Erstellen des vectors draufgeht. Kein Wunder, dass der vector<bool> so gut abschneidet, denn das geht natürlich deutlich schneller wenn man weniger Speicher braucht.

    Auch wieder so ein Aspekt, an den man gar nicht gedacht hätte, ohne dass man es ausprobiert. War wohl doch nicht die Lokalität.

    Immer schön vorsichtig sein, bei der Mikrooptimierung.


  • Mod

    volkard schrieb:

    Bis 1e8
    bool: 13.77
    char: 21.11

    Und der Prozessor ist nicht neu. AMD Sempron 3000+ @ 1800MhZ.

    Dass ist langsamer als mein atom 330 (1.6GHz) - sollte eigentlich nicht passieren. Sind denn auch alle Checks deaktiviert? Visual C++ hat da ja eine Menge standardmäßig aktiv.


Anmelden zum Antworten