Speicherort von Konstanten



  • Hallo zusammen,

    ich stehe wohl gerade auf dem Schlauch 😕 und ihr könnt mir sicher sofort helfen. Ich habe in meinem Programm ein paar ziemlich große Tabellen (>= 10000 Elemente). Diese Tabellen habe ich als konstant definiert, da sie nur als Lookup-Tables gebraucht werden:

    const int tabelle1[10000] = { /* 10000 Einträge /* };
    const int tabelle2[10000] = { /* 10000 Einträge /* };
    
    // Aufruf
    if( tabelle1[ index ] ){
      // tuwas
    }
    else if( tabelle2[ index ] ){
      // tuwas anderes
    }
    

    Wenn es sich dabei um Variablen handeln würde, würde ich den Speicherplatz für diese Tabellen nicht auf dem Stack sondern auf dem Heap (mit new[]) anfordern, da ich Angst hätte, dass mir auf dem Stack der Speicher ausgeht.
    Ich bin mir jetzt gerade nicht sicher, ob ich mir mit o.g. Code den Stack (unnötig) vollhaue oder Konstanten sowieso nicht auf dem Stack gespeichert werden. Könntet ihr also bitte mal kurz meine bescheidenen C++ Kenntnisse auffrischen und mir sagen, wo Konstanten gespeichert werden und wie ich am besten große konstante (= zur Kompilierzeit bekannte) Datenmengen abspeichere?

    Vielen Dank!



  • Manche Compiler legen für konstante Daten ein eigenes Daten Segment an.
    Auf keine Fall aber kommen sie auf den Stack.
    Konstante Daten auf den Heap ist ein kleiner Widerspruch, hm?
    new = dynamisches allokieren von Speicher auf dem Heap!

    Ob das ganze geschickt oder nicht geschickt ist, weißt allein du. Was ist denn drin?
    Generell fragt man sich natürlich, was einer Speicherung in einer seperaten Datei im Wege stehen könnte.



  • Diese nicht objekt orientierte Wege sind einfach nur furchbar.
    Mach eine Klasse dafür.

    public class Konstanten
    {
        public:
            Konstanten()
            {
                tabelle = new int[50000];
                setupTable();
            }
    
            ~Konstanten()
            {
                delete[] tabelle;
            }
    
            int get(int index)
            {
                return (*tabelle)[index];
            }
    
        private:
    
            int* tabelle;
    
            void setupTable()
            {
                /* Tabelle mit Daten füllen */
            }
    }
    

    oder Singleton-Pattern:

    public class Konstanten
    {
        public:
            Konstanten getInstanz()
            {
                if ( intstanz == NULL ) instanz = new Konstanten();
                return instanz;
            }
    
            int get(int index)
            {
                return (*tabelle)[index];
            }
    
        private:
    
            static Konstanten instanz;
    
            int* tabelle;
    
            Konstanten()
            {
                tabelle = new int[50000];
                setupTable();
            }
    
            ~Konstanten()
            {
                delete[] tabelle;
            }
    
            void setupTable()
            {
                /* Tabelle mit Daten füllen */
            }
    }
    

    Zugriff ist:

    int maint()
    {
        int x = Konstante.getInstanz().get(100);
    }
    

    edit: hab den Destruktor vergessen.



  • Hallo nirsaja, DEvent,

    erstmal vielen Dank für eure Antworten. Ich hatte schon irgendwie vermutet, dass die Konstanten nicht auf dem Stack liegen, war mir jetzt aber grad nicht so sicher. Mit Speicherung in einer separaten Datei meinst du aber auch, dass ich die Daten zur Laufzeit einlese und dann in einem (dynamisch mit new[] angefordertem) Array speichere, oder wie DEvent vorgeschlagen hat in einer eigenen Klasse speichere?

    Ich will nur möglichst schnellen Zugriff auf die einzelnen Werte haben, da in meinem Programm ausgehend von ein paar Startwerten u.a. über die Inhalte der Tabellen ein Endwert ermittelt wird. Und da ich immer wieder mehrere Millionen Kombinationen prüfen muss (und dies natürlich möglichst schnell machen will), dachte ich die von mir gewählte Form wäre vorteilhaft (weil schnell).

    Wie sieht es denn mit der Performance bei den Klassenzugriffen aus, sind die ungefähr genauso schnell wie meine "Quelltext"-Konstanten? Schöner ist es mit so einer Klasse ja schon, aber für mich zählt in erster Linie die Geschwindigkeit.

    Gruß



  • Ja, sobald du einiest, musst du wieder dynamischen Speicher verwenden - es sei denn du weißt wie viele Werte du einlesen musst. Dann hat das Einlesen allerdings auch keine großen Vorteile mehr.
    DEvents Lösung ist objektorientiert, ich halte aber trotzdem nichts davon.
    Der Overhead durch die vielen get Aufrufe(hier nichtmal inline!) ist enorm und ich bezweifle, dass die Lesbarkeit dadurch verbessert wird.
    Wenn du sicher weißt, dass du nicht mehr und nicht weniger als deine 2 * 10000 Konstanten hast, würde ich deine Lösung vom Anfang nehmen.

    Es ist konstant(DEvents Lösung garrantiert (noch) nichtmal das)
    Es entspricht höchst wahrscheinlich mit dem Indexoperator der Logik deiner Berechnung
    Es ist schnell
    Es verbraucht garrantiert minmal Speicher



  • @DEvent
    Und nur so btw... du schreibst etwas Java-like Syntax, dereferenzierst, wo es nichts zu dereferenzieren gibt und vergleichst nicht-pointer auf Gleichheit mit 0!



  • Es ist konstant(DEvents Lösung garrantiert (noch) nichtmal das)
    Es entspricht höchst wahrscheinlich mit dem Indexoperator der Logik deiner Berechnung
    Es ist schnell
    Es verbraucht garrantiert minmal Speicher

    All diese Vorteile hat die objectorientierte Lösung ebenfalls.
    Man kann ja in C++ auch den Indexoperator überladen.

    Und was bitte ist an meiner Klasse nicht konstant ? Du kannst die Werte nicht ändern. Sie werden einmal in der Methode setupTabelle() gesetzt und nie wieder geändern.



  • Und was bitte ist an meiner Klasse nicht konstant

    Es soll sogar ein Schlüsselwort geben um sowas auszudrücken. Das ist bei der Lösung von unserem Auf-dem-Schlauch-Steher sehr gut möglich.

    Aus den genannten Gründen ist deine Lösung nicht schnell.
    Wenn du jetzt auch noch den Indexoperator überlädst gute Nacht...
    Ob

    Konstante.getInstanz().get(100)
    

    oder

    tabelle1[100]
    

    "intuitiver" ist ist ja wohl klar. Wer rechnet nach getInstanz() noch mit .get(100). Ich verstehe deine Absicht, aber hallo?!...
    Und mit dem Speicherverbauch werd ich gleich pingelig! Ich meine, dass diese Lösung nicht mehr Speicher als den für die Werte selbst braucht. Du brauchst in deinem Beispiel 4Byte mehr 😃 Und im zweiten sogar noch mehr :D:D:D

    Zu den Vorzügen von C++:
    C++ ist objektorientiert - Aber es ist verdammt noch mal auch maschienennah!
    Hier wird objektorientiert modelliert wo es nicht nötig ist!



  • Hallo Ihr beiden,

    nochmal vielen Dank für Eure Antworten. Da ich nur eine festgelegte Anzahl von Tabellen mit bekannter Anzahl an Einträgen habe (die sich auch nie mehr ändern werden), werde ich wohl bei meiner Lösung bleiben. Von der Performance her ist es ok und ich denke, man kann den Quelltext auch verstehen.

    Der Inhalt der Konstanten kann auch aus dem Index berechnet werden (anhand weiterer Parameter) und dies habe ich auch erst gemacht, jedoch ist mein Programm mit den vorberechneten Tabellen deutlich schneller (bei ca. 100 Millionen Iterationen anstelle von ca. 50s nur ca. 10s).

    Die objektorientierten Vorzüge von C++ versuche in dem Teil unterzubringen, mit dem später gearbeitet wird (die Konstanten wird man als Benutzer meiner Klassen nie zu Gesicht bekommen).

    Schönen Abend noch!



  • Sehr gut. Ich finde deine Entscheidung ist gut begründet!



  • nirsaja schrieb:

    Aus den genannten Gründen ist deine Lösung nicht schnell.
    Wenn du jetzt auch noch den Indexoperator überlädst gute Nacht...

    Jeder halbwegs vernünftiger Compiler wird daraus eine inline-Aufruf basteln.
    Immer diese Vorurteile das objectorientiert gleich langsam ist. Beweis das erstmal. Aus dem gleichen Grund kommt auch der Vorurteil Java = Langsam, obwohl dies nie bewiesen wurde.

    Indexoperator:

    public class Konstanten 
    { 
        public: 
            Konstanten() 
            { 
                tabelle = new int[50000]; 
                setupTable(); 
            } 
    
            ~Konstanten() 
            { 
                delete[] tabelle; 
            } 
    
            int get(int index) 
            { 
                return (*tabelle)[index]; 
            } 
    
            int operator[] (int index)
            {
                return (*tabelle)[index]; 
            }
    
        private: 
    
            int* tabelle; 
    
            void setupTable() 
            { 
                /* Tabelle mit Daten füllen */ 
            } 
    }
    

    nun kann man auch

    Konstanten konst;
    int i = konst[100];
    


  • OO hat nix mit der geschwindigkeit zu tun ... wer so argumentiert, mit dem brauch man auch ned uber probleme von Speicher alloc und ablage von Konstanten diskutieren, da fehlt einfach noch die Reife ^^ (und ja, Java ist langsam ! ^^ )

    @Auf-dem-Schlauch-Steher
    egal wo du es ablegst, in ne richtige Saubere OO Form kann mans immer bringen, das sollt nie das argument sein.
    Ich wuerd es von 2 Faktoren abhaengig machen ....

    - Groesse der Daten.
    Linux iss da was flexiebler, aber bei windows gibts schon grenzen, nur sind die auch sehr hoch. Wenn sie aber trotzdem ins DS / stack passen, warum ned ?

    - Konstantheit / Wartung
    Wie konstant sind deine "konstanten". Wie oft muss man die aenderen (glaub nur vorberechnete Ergebnisse komplizierter mathematische Formeln sind wirklich konstant ). In welcher Form liegen deine Konstanten vor ? Ist der Aufwand vertretbar, bei ner aenderung die .h oder das ress file anzupassen und die Anwendung neu zu uebersetzen und vor allem neu zu verteilen ?

    ne andere Sache ist, deine daten sind gross .... gibt es mehrere Programme oder mehrere Instanzen die deine Daten brauchen? Wenn ja, einige intelligene BS schaffen es , das CodeSegment und Datensegment(fuer konstante Daten) beim Laden einer Dynamischen Lib nur einmal in den Speicher zu hauen. das koennte man gewinnbringend nutzen, da ist aber verraussetzung das die daten ins Datensegment bekommst, in ned in den Stack ...
    Und trotzdem hasst den zeitlichen vorteil eines dynamischen ladens durch den loader (und auch den overhaed ^^)

    Ciao ...


Anmelden zum Antworten