Problem(e) mit 64-Bit-Typen


  • Mod

    Kannst du mal den genauen Code hinschreiben den du derzeit benutzt und was genau für Probleme auftreten? Wenn man das Problem bei sich auf dem Rechner nachvollziehen kann (wir können nämlich Debugger benutzen :p ), ist es gleich viel einfacher zu helfen, als nur zu spekulieren.


  • Mod

    Schlitzauge schrieb:

    Bei den gerade so implementierten 128-Bit-Int-Typen sehe ich ja noch ein Fehlverhalten ein, aber 64-Bit ist doch schon seit Jahren erprobt und hat sich bewährt.
    Grundsätzlich verstehe ich einfach nicht das Problem. Ist es Eigenverschulden, ne Macke des Compilers, der Compiler-Optionen, der verwendeten Libs, oder oder oder???

    Ich würde auf eine Macke tippen, wobei es natürlich schon etwas mit 128bit-Typen zu tun hat.

    2.14.2 Integer literals [lex.icon]
    ...
    3 If an integer literal cannot be represented by any type in its list and an extended integer type (3.9.1) can represent its value, it may have that extended integer type. If all of the types in the list for the literal are signed, the extended integer type shall be signed. If all of the types in the list for the literal are unsigned, the extended integer type shall be unsigned. If the list contains both signed and unsigned types, the extended integer type may be signed or unsigned. A program is ill-formed if one of its translation units contains an integer literal that cannot be represented by any of the allowed types.

    9223372036854775808 passt in einen vorzeichenbehafteten 64-bit Integer nicht herein, und das Literal muss seiner Form nach auf jeden Fall vorzeichenbehaftet sein. Folglich muss ein größerer Integertyp her.



  • SeppJ schrieb:

    Kannst du mal den genauen Code hinschreiben den du derzeit benutzt und was genau für Probleme auftreten? Wenn man das Problem bei sich auf dem Rechner nachvollziehen kann (wir können nämlich Debugger benutzen :p ), ist es gleich viel einfacher zu helfen, als nur zu spekulieren.

    Naja, hab ich doch bereits. Gleich zu Beginn. Ich habe das Ganze nur um die INT-Literale ergänzt. Der Form halber, hier nochmal der aktuelle Code:

    #include <climits>
    #include <cstdint>
    #include <cinttypes>
    #include <cfloat>
    #include <cwchar>
    #include <string>  //std::string
    #include <string.h>
    #include <cstring>
    #include <cstdlib>
    #include <cstdio>
    #include <iostream>
    #include <cmath>
    
    #ifdef _OPENMP
    #include <omp.h>
    #endif
    
    using namespace std;
    
    int main(int argc, char *argv[])
    {
      uint64_t maxof = 0-1;
      const uint64_t NumValues = <Wert>ULL;
      uint64_t NumIterSingle = 0ULL;
    
      for(uint64_t i=0ULL;i<NumValues;i++)
      {
        NumIterSingle++;
      }
      printf("Benötigte Iterationen - SERIELL:  %"PRIu64"\n",NumIterSingle);
      cout << "Referenz:                         " << NumValues << '\n';
      cout << "MaxOf uint64_t:                   " << maxof << '\n';
    
      getchar();
      return 0;
    }
    

    Compile (Release):

    g++ -std=c++0x -m64 -fopenmp -Wall -Wextra -pedantic -pedantic-errors -O3 -s source -o dest
    

    Compile (Debug):

    g++ -std=c++0x -m64 -fopenmp -Wall -Wextra -pedantic -pedantic-errors -g -ggdb3 source.cpp -o dest
    

    g++-Version (gcc):

    gcc version 4.4.5 20110214 (Red Hat 4.4.5-6) (GCC) x86-64
    

    Zur Erinnerung: setze man folgende Werte für "NumValues" ein:

    bis 9223372036854775807      // keine Probleme
        9223372036854775808      // Problem
    ab  9223372036854775809      // keine Probleme
    

    Übersetzt wurde dabei mit Compile (Release).
    Wenn ich den Optimierungsschalter "-O3" weglasse, tritt das beschriebene Phänomen bei allen Zahlen auf. Lediglich bei Anwendung von "-O3", macht sich mir ein Fehlverhalten bemerkbar (siehe 9223372036854775808).

    Wie schon erwähnt, scheint der Compiler lediglich beim Wert 9223372036854775808 für NumValues, nicht richtig zu optimieren, da die Ausführungszeit Ewigkeiten dauert. Es verwirrt mich dahingehend, dass es nur bei dem Wert 9223372036854775808 auftritt (als wenn da nicht optimiert wurde). Alle anderen Werte, sprich bis 9223372036854775807 und ab 9223372036854775809 bis einschließlich dem Maximalwert von unsigned 64-Bit-INT: 18446744073709551615, funktionieren (das Programm wird also in kürzester Zeit in einem Rutsch abgearbeitet).

    camper schrieb:

    Ich würde auf eine Macke tippen, wobei es natürlich schon etwas mit 128bit-Typen zu tun hat.

    Keine 128-Bit-Typen. Diesmal setze ich ausschließlich 64-Bit-Typen ein. Dabei ist es übrigens egal, ob ich die festen Typen a´la [u]int64_t oder [unsigned] long long int verwende. Das Fehlverhalten bleibt das Gleiche. Das verwundert natürlich nicht, da die festen Typen auf 64-Bit-Systemen lediglich eine Typdefinition der normalen Typen sind (in meinem Fall also von unsigned long long int. Deshalb auch das Literal ULL).

    Die Aktuelle Version von g++ ist eigentlich die 4.6.1 bzw. gar schon die Version 4.7. Wie oben erwähnt, ist die auf meinem System Aktuelle, noch die 4.4.5 (aktuellste aus den Paketquellen).

    Die Frage ist nun, ob das ein Eigenfehler von mir ist oder technisch bedingt bzgl. des Compilers, der Compiler-Option(en) und / oder der verwendeten Bilbiotheken?

    Kann jemand meinen Fall unter Verwendung obigen Codes und Compiler-Version+Schlatern überhaupt reproduzieren und ggfl. als Vergleich das Ganze mit einem anderen Compiler oder neuerer Compiler-Version ausprobieren, ob es das Fehlverhalten da immer noch gibt? THX!
    Und falls jemand das auf einen technischen Hintergrund beschränken kann, wo man selbst halt keine Handhabe mehr hat, was hättet Ihr dann noch für Tipps für mich, wie ich das Problem IRGENDWIE umschiffen kann? Ich muss nämlich definitiv mit solchen Größenordnungen hantieren.

    camper schrieb:

    9223372036854775808 passt in einen vorzeichenbehafteten 64-bit Integer nicht herein, und das Literal muss seiner Form nach auf jeden Fall vorzeichenbehaftet sein. Folglich muss ein größerer Integertyp her.

    Ob mit oder ohne Literale, das Problem bleibt.
    Doch scheint mein Compiler diese Zahlen auch ohne Literale zu schlucken. Spuckt dann zwar Warnungen, aber es funktioniert. Wie Ihr oben seht, hab ich es dennoch der Richtigkeit halber mit Literale gemacht. Aber zum Vergleich: 9223372036854775809 und aufwärts funktionieren auch ohne Literale.

    Besten THX und Grüße

    Schlitzauge 🙂
    PS: Debugger hab ich auch schon verwendet, nur geht da alles reibungslos von statten. Ich kann auch mit dem Debugger nichts finden, was dieses Fehlverhalten -wenn es denn eins ist- mir erklären würde. Vlt. übersehe ich aber auch nur einfach was.


  • Mod

    Ich hätte halt gedacht, dass du deinen Code mit den Tipps mal etwas zusammengestaucht hättest auf ein Minimalbeispiel. Zum Beispiel:

    #include <cstdint>
    #include <iostream>
    
    using namespace std;
    
    int main()
    {
      const uint64_t NumValues = 9223372036854775809ULL;
      uint64_t NumIterSingle = 0ULL;
    
      for(uint64_t i=0ULL;i<NumValues;i++)
      {
        NumIterSingle++;
      }
      cout <<"Benötigte Iterationen - SERIELL: " << NumIterSingle << '\n';
      cout << "Referenz:                         " << NumValues << '\n';
    
      return 0;
    }
    

    Und tja, dann kann er halt für diesen Wert die Schleife nicht optimieren. Da müssteste mal einen Compilerentwickler fragen, was da intern geschieht. Ich kann's jedenfalls nachvollziehen bei mir. Wobei der gcc der einzige Compiler ist, der die Schleife überhaupt optimiert. Intel und Sun-Compiler versagen da bei mir total. Du kannst das ja mal als Bugreport einschicken.



  • Darf man fragen, was für ein Compiler (also GCC/g++) Du verwendet hast?

    Ich hab das Ganze jetzt mit den 128-Bit-Typen getestet.
    Also statt uint64_t, halt __uint128_t. Da funktionierts komischerweise. Also mit allen Werten vom Typ 64-Bit-unsigned-int (uint64_t).

    Zwar nicht gerade das Gelbe vom Ei, da noch kein Standard, aber immerhin.
    Dennoch wäre ich für eine Antwort/Lösung sehr dankbar. Da ich anscheinend der Einzige bin, bei dem das Phänomen auftritt, vermute ich einen technischen Compilerfehler. Ich lasse mich natürlich gerne auch eines Besseren belehren. Ich werds sicherheitshalber mal als Bugreport einschicken. Wäre sehr hilfreich, wenn jemand mit einem aktuellen g++ (4.6.1 / 4.7) das mal testen könnte. Bei mir liegt gerade keiner vor.

    Grüße

    Schlitzauge 🙂


  • Mod

    Ich habe getestet:
    gcc 4.1 (Optimiert nicht bei besonderem Wert)
    gcc 4.4 (Optimiert nicht bei besonderem Wert)
    gcc 4.6 (Optimiert nicht bei besonderem Wert)
    Intel 11 (Optimiert nie)
    Intel 12 (Optimiert nie)
    Sun 12 (Optimiert nie)
    Mit 128-Bit Typen exerziere ich das aber nicht auch noch durch. Ich hatte aber noch AMD open64, Pathscale und PGI in verschiedenen Versionen anzubieten.

    edit2: Aus Interesse:
    PGI 8: (Optimiert nie)
    Pathscale 3.2 (Optimiert nicht bei besonderem Wert)
    Open64 4.2 (Optimiert nicht bei besonderem Wert)

    Da scheint doch etwas fundamentaleres vorzuliegen. Oder die Compilerbauer schreiben voneinander ab.


  • Mod

    Der Effekt tritt nicht auf, wenn != statt < für die Schleifenbedingung verwendet wird. Ansonsten verhält sich gcc hier sowieso etwas seltsam. Mit -O2 wird die Schleife optimiert, mit -O und allen zusätzlichen Optimierungsoptionen, die laut Manual durch -O2 aktiviert werden, dagegen nicht (getestet mit g++ 4.5.3, 4.6.2 und 4.7.0 vom 22.10.)


  • Mod

    Ich habe oben noch mehr Ergebnisse reineditiert die ganz interessant sind. Ach, jetzt spielt auch noch der Optimierungsgrad eine Rolle. 😮

    Meine Tests waren alle mit einem einfachem -O3 ohne Zusatzoptionen.


  • Mod

    SeppJ schrieb:

    Ach, jetzt spielt auch noch der Optimierungsgrad eine Rolle. 😮

    Ich wollte eigentlich herausfinden, an welcher Stelle der Compilierung die Schleife durch Konstanten ersetzt wird. Naiiverweise nahm ich an, dass dafür selektives Setzen von Optimierungsoptionen weiterhilft.


  • Mod

    Ach, ich hatte dich falsch verstanden. Ich hatte gedacht du meintest, mit O2 würde es auch beim kritischen Wert optimiert, mit O3 nicht mehr.



  • @SeppJ:
    Tritt das beschriebene Phänomen bei all Deinen getesteten Kompilern auf?
    Also nicht, ob optimiert wurde, sondern eben das beschriebene "alles optimiert, bis auf Anwendung des EINEN Wertes"? Primär interessieren mich da die GNU-Compiler (GCC/g++).

    camper schrieb:

    Der Effekt tritt nicht auf, wenn != statt < für die Schleifenbedingung verwendet wird.

    Tatsache. Ja gut, auf Assembler-Ebene eine Ungleichheit abzufragen, ist bei weitem nicht so kompliziert wie < bzw. >.
    Die Maximalgrenze von signed INT-64-Bit ist 9223372036854775807. Die 9223372036854775808 nur negativ als kleinster Wert: -9223372036854775808.
    Meine Vermutung tendiert daher noch mehr Richtung Fehlverhalten/Fehlerhafte Implementierung beim Compiler.

    Gibts jetzt auch noch eine Möglichkeit das Ganze einer parallelen OMP-Schleife beizubringen? Denn != nimmt OMP nicht an: 🤡

    #pragma omp parallel reduction(+: NumIterParallel) shared(Threads)
      {
        #pragma omp for schedule(static)
        for(uint64_t k=0ULL;k<NumValues;++k)
        {
          NumIterParallel++;
          Threads[omp_get_thread_num()]++;
        }
      }
    

    Compiler-Output:

    AkkuInc_main.cpp:57: error: invalid controlling predicate
    

    Aber das jetzt nur so nebenbei.


  • Mod

    Schlitzauge schrieb:

    @SeppJ:
    Tritt das beschriebene Phänomen bei all Deinen getesteten Kompilern auf?
    Also nicht, ob optimiert wurde, sondern eben das beschriebene "alles optimiert, bis auf Anwendung des EINEN Wertes"? Primär interessieren mich da die GNU-Compiler (GCC/g++).

    Bei denen, bei denen ich geschrieben habe "Optimiert nicht bei besonderem Wert" wird bei 9223372036854775808 nicht optimiert, bei 9223372036854775807 und 9223372036854775809 aber schon. Bei "Optimiert nie" wurde in keinem der drei Fälle optimiert.


Anmelden zum Antworten