Pointer als "read only" übergeben.



  • Hallo zusammen,

    ich arbeite auf einer Controller-Plattform die alle 100 mys einen Regelalgorithmus durchführt. Die Aufrufreihenfolge und übergebenen Parameter bleiben dabei gleich.

    Die Regelung ist in Funktionsgruppen unterteilt, die auch auf anderen Systemen unter anderem Kontex zum Einsatz kommen. Das Prinzip ist jedoch immer das Gleiche:

    a.process( "vom Sensor" );
    b.process( a.getValue() );
    c.process( b.getValue() );
    "zum Aktor" = c.getValue();
    

    a, b, c sind Objekte der Regelungsklassen. Die getValue()-Methoden sind stellvertretend für die ganzen Getter der Ergebnisse, die in den Klassen als private Attribute angelegt sind. Das kleine Beispiel ist natürlich stark vereinfacht.

    Der Grund für diesen Thread betrifft die Übergabeparameter an die process-Methode. Diese sind in der realen Anwendung zahlreich. Um die Übersicht zu erhöhen habe ich die Idee, den Pointer auf die benötigten Ergebnisse zur Initialisierung zu übergeben. In etwa so:

    Class A {
        float x;
    public:
        const float* getX( void ) {
            return &x;
        }
    }
    
    Class B {
        const float* y;
    public:
        void setY( const float* y ) {
            this->y = y;
        }
        float process( void ) {
            return 2.f * (*y);
        }
    }
    

    Die Übergabe würde einmalig zur Initialisierung erfolgen

    b.setY( a.getX() );
    

    Anschließend könnte b permanent mit dem Wert von A::x arbeiten.

    Jetzt frage ich mich jedoch folgendes:
    1. Wie "sicher" ist A::x gegenüber "äußeren" Zugriffen. A::x darf nicht außerhalb von A geändert werden, weder der Wert noch die Adresse. So eine Art "read only"
    2. Ist die Variante ineffizienter (bezüglich Rechenzeit) als die Alternative

    float process( const float y ) { return 2.f * y; };
    

    Insgeheim habe ich die Hoffnung, dass es sogar effizienter ist, da der Getter-Aufruf und die Kopierfunktion durch call by value gespart wird.

    Viele Grüße
    Zisko



  • Ich verstehe Deine Frage nicht so ganz, aber ich kann Dir sagen, dass const -copy für Funktionsparameter keinen Sinn macht (so weit ich weiß).

    Wenn du sehr viele Funktionsparameter übersichtlicher übergeben willst, wäre es doch eine Überlegung wert einzelne Werte in ein struct zu packen und ein solches zu übergeben.

    LG



  • const-copy zur Sicherheit, damit der Parameter in der Funktion nicht geändert wird.

    Wenn ich ein struct verwende, dann habe ich bei jedem Interrupt-Aufruf wieder die gleichen Kopievorgänge, die ich vermeiden möchte. Der Unterschied ist nur, dass ich sie von der Parameterliste der Methode irgendwo anders hin verlagere.



  • weiß nicht ob ich dich richtig verstehe...
    vll sind inline-funktionen das was du suchst? 🙂

    Zisko schrieb:

    const-copy zur Sicherheit, damit der Parameter in der Funktion nicht geändert wird.

    Wenn ich ein struct verwende, dann habe ich bei jedem Interrupt-Aufruf wieder die gleichen Kopievorgänge, die ich vermeiden möchte. Der Unterschied ist nur, dass ich sie von der Parameterliste der Methode irgendwo anders hin verlagere.

    ansonsten hätte ich jetzt auch gesagt nimm structs, da diese die " parameterübergabe an eine methode übersichtlicher gestalten"...
    da könntest einfach alles drin sammeln und dann schreiben (mit nur einem aufruf -> übersichtlicher) 🙄

    hoff hat geholfen
    lg



  • Erstmal danke für die Antworten. Ich glaube die Überschrift des Beitrages ist etwas ungeschickt gewählt. Mir geht es in erster Linie um die zwei gestellten Fragen.

    Also ob der "Schreibschutz" gewährleistet ist (wenn nein, wie sorge ich dafür). Das ist mein Hauptanliegen.

    Auch wichtig: ist die Variante gleichwertig "schnell" oder sogar performanter, da ich in Summe hunderte Kopiervorgänge durch call by value spare.



  • der schreib-"schutz" ist so sicher wie ein zeiger-auf- const sein kann. ändern sollte man es nicht, aber const_cast wäre erlaubt und könnte den schutz aufbrechen. das wird aber wohl niemand ernsthaft tun wollen.

    viel eher stellt sich die frage, ob du auch garantieren kannst, dass es den "geteilten" speicher während der gesamten lebenszeit der objekt auch wirklich gibt.

    bezüglich gleichwertig schnell: das musst du messen, denn es hängt von deinem konkreten anwendungsfall und optimierungsmöglichkeiten (für den compiler) ab. grundsätzlich: herumreichen auf dem stack vs. indirektion; ohne mehr zu wissen, würde ich ja fast meinen, dass das direkte übergeben eines parameters leichter zu optimieren wäre... schau dir die assembly-ausgabe an und: miss nach.



  • Für einfache Werte (ein float, ein double, ein int, ...) ist das Durchreichen von Werten by-value meist schneller als der Zugriff über Zeiger.
    Weil du den Wert dadurch nicht "in den Speicher zwingst".

    Hängt aber natürlich von ein paar Faktoren ab, u.A.:
    * Der Ziel-Architektur
    * Der verwendeten Calling-Convention
    * Dem verwendeten Compiler
    * Den verwendeten Compiler Flags (welche Optimierungen du forderst/erlaubst etc.)
    * Wie viele Parameter die "Process" Funktion zusätzlich noch hat
    ...

    Wenn es wirklich wichtig ist: vergleiche (mach nen Benchmark). Es sollte ja vermutlich nicht sehr schwer sein den Code so zu schreiben dass du beide Varianten relativ einfach gegeneinander austauschen kannst.

    Ansonsten wäre es vermutlich gut wenn du hier so konkret wie möglich zeigst welche Daten da zwischen den "Regelungsklassen" übergeben werden. Nicht dass du es hier mit einem einzigen float zeigst, dann Tips die auf einen einzigen float zurechtgeschnitten sind bekommst, und in Wirklichkeit willst du dann 1000 floats pro Durchgang transportieren.



  • ps

    Zisko schrieb:

    alle 100 mys

    Was meinst du mit mys - ms oder µs 😕



  • Ja sind μs 🙂

    Auf den Rat hin habe ich gestern einen Benchmark mit einer Klasse gestartet. Diese zeichnet sich dadurch aus besonders viele Werte zu erhalten, jedoch relativ wenig Funktions-Code auszuführen.

    Dieser Code hat Beispielsweise aus drei andereren Klassen über Getterfunktionen insgesamt 16 float-Werte erhalten.

    Mit Pointern konnte ich die Aufrufzeit von 2.1μs auf 1.2μs reduzieren. Ich traue dem Braten jedoch noch nicht ganz, da es mir doch etwas viel erscheint. Den Test werde ich außerhalb der Unit-Test-Umgebung nochmal unter realen Bedingungen wiederholen.



  • Kannst du den Code mal zeigen? Also nicht unbedingt die Berechnungen, falls du Bedenken hast die zu posten, aber so dass man 1:1 die Struktur sieht wie und wo da Daten per Getter bzw. per Zeiger gelesen werden.
    Dann könnte ich mir mehr darunter vorstellen, bzw. dann kann dir vielleicht auch jemand nen Tip geben.

    Ansonsten, was du noch versuchen kannst, ist die Werte einfach in einem dicken struct mit den 16 floats by-value zu übergeben.


Anmelden zum Antworten