OMG! Man lernt nie aus. Type-qualifiers und static in array declarators ab C99



  • Gestern blätterte ich im C99 Rationale und erblickte im Abschnitt 6.7.5.2 Array declarators auf Seite 90 etwas mir vollkommen Unbekanntes:

    A new feature of C99: The static storage class specifier and any of the type-qualifiers,
    restrict, const or volatile, can appear inside the [ and ] that are used to declare an
    array type, but only in the outermost array type derivation of a function parameter.

    Sowas kam mir noch nie unter. Nachdem der erste Schock überwunden war, wurde mir der Sinn dahinter aber schnell klar. Da Arrays als Funktionsparameter in Zeiger auf ihr erstes Element zerfallen, sollte es auch möglich sein, diese mit type-qualifiers zu versehen. Der Rationale bringt im Anschluss daran ja auch gleich einige Beispiele und erklärt, was man hier mit static anfangen kann:

    The static keyword provides useful information about the intent of function parameters.
    Consider:

    void fadd(double *a, const double *b)
    {
        int i;
        for (i = 0; i < 10; i++) {
            if (a[i] < 0.0)
                return;
            a[i] += b[i];
        }
        return;
    }
    

    It would be a significant advantage on some systems for the translator to initiate, at the
    beginning of the function, prefetches or loads of the arrays that will be referenced through the
    5 parameters. There is no way in C89 for the user to provide information to the translator about
    how many elements are guaranteed to be available.

    In C99, the use of the static keyword in:

    void fadd(double a[static 10], const double b[static 10])
    {
        int i;
        for (i = 0; i < 10; i++) {
            if (a[i] < 0.0)
                return;
            a[i] += b[i];
        }
        return;
    }
    

    guarantees that both the pointers a and b provide access to the first element of an array
    containing at least ten elements. The static keyword also guarantees that the pointer is not
    NULL and points to an object of the appropriate effective type. It does not, however, guarantee
    that a and b point to unique, non-overlapping objects. The restrict keyword is used for
    that purpose as in:

    void fadd(double a[static restrict 10],
                const double b[static restrict 10])
    {
        int i;
        for (i = 0; i < 10; i++) {
            if (a[i] < 0.0)
                return;
            a[i] += b[i];
        }
        return;
    }
    

    This function definition specifies that the parameters a and b are restricted pointers. This is
    information that an optimizer can use, for example, to unroll the loop and reorder the loads and
    stores of the elements referenced through a and b.

    The const keyword can be used to indicate that the pointer will always point to the same array
    object. The function declaration:

    void f(double x[const], const double y[const]);
    

    is another way of declaring:

    void f(double * const x, const double * const y);
    

    There does not appear to be much value in using volatile to qualify an array function
    parameter.

    Was haltet ihr davon? Benutzt ihr dieses Feature? Oder sind bei euch Funktionsparameter schon immer gleich Zeiger anstatt Arrays gewesen?


  • Mod

    Das sind extreme Mikrooptimierungen. Wann immer ich mal damit rumgespielt habe, war ich vom Ergebnis enttäuscht. Sprich: Es bringt wohl eher etwas in konstruierten Beispielen, in echten Programmen bei mir bisher nicht.



  • Hm.
    Ich hab's nicht ausprobiert, aber ich bin überzeugt dass restrict - bei bestimmten Funktionen, und wenn Inlining nicht möglich ist - schon viel bringt.
    Die anderen vermutlich nicht.



  • Das sind extreme Mikrooptimierungen. Wann immer ich mal damit rumgespielt habe, war ich vom Ergebnis enttäuscht. Sprich: Es bringt wohl eher etwas in konstruierten Beispielen, in echten Programmen bei mir bisher nicht.


  • Mod

    hustbaer schrieb:

    Ich hab's nicht ausprobiert, aber ich bin überzeugt dass restrict - bei bestimmten Funktionen, und wenn Inlining nicht möglich ist - schon viel bringt.

    Es ist nicht so, dass ich mir nicht vorstellen kann, dass es solche Fälle gibt, es ist, dass diese irgendwie nie vorkommen. Konstruiertes Beispiel eben. Wenn ich mal einen Grafikkartentreiber schreibe, mag das ganz anders aussehen, aber wer macht das schon? Meine Programme sind durch andere Probleme limitiert und meine innersten Schleifen fast immer inline, so dass es selbst theoretisch nichts bringt. Gerade letzter Punkt ist wohl wichtig: Es ist heutzutage derart einfach, die kritischen Schleifen inline zu halten, sei es durch Compilerfeatures (Optimierung mit Blick auf das ganze Programm) oder Sprachfeatures (Templates), dass eigentlich immer bekannt ist, auf welches konkrete Objekt ein Zeiger zeigt. Das ist wesentlich einfacher, als sich selber darum zu kümmern.



  • Cool, die kannte ich noch nicht!
    Statt nur Optimierungsmöglichkeiten zu bieten, erlauben die qualifier doch auch compile time Warnungen/Fehler. Ich habe z.B. immer int* const a statt int a[] geschrieben, da ich generell alles const’e was nich geändert wird und die letztere Syntax elaubte es mir nicht den Zeiger zu const’en. Nun kann ich int a[const] schreiben, wenn ich deutlich zu machen möchte, dass es sich um ein Array handelt und der Zeiger nicht verändert werden soll.
    Und mit dem static dazu ermögliche ich es dem Compiler auch mich zu warnen, wenn NULL als Parameter übergeben wird und dieser nicht NULL sein darf (leider ersetzt das nicht die Laufzeitprüfung auf NULL ). Für Angabe einer Mindestgröße halte ich es hingegen für nicht notwendig, da man eigentlich immer die Arraygröße als separaten Parameter an Funktionen übergibt (höchstens wieder für Compilerwarnungen, wenn man wirklich eine Mindestgröße braucht, mir fällt da aber auf Anhieb kein Beispiel ein).
    restrict ist genau wie const : immer, wenn möglich.



  • Hm, warum geht das in C++ nicht (mehr)? Konnte sich das nicht durchsetzen oder warum hat man hier auf abwärtskompatibilität verzichtet?



  • Biolunar schrieb:

    leider ersetzt das nicht die Laufzeitprüfung auf NULL

    Die, die dir Schutzverletzung ausführt, wenn man drauf zugreift? Dachte, die sei recht kostenlos.

    Biolunar schrieb:

    Für Angabe einer Mindestgröße halte ich es hingegen für nicht notwendig, da man eigentlich immer die Arraygröße als separaten Parameter an Funktionen übergibt (höchstens wieder für Compilerwarnungen, wenn man wirklich eine Mindestgröße braucht, mir fällt da aber auf Anhieb kein Beispiel ein).

    Bei Spielen wie Schach hat man [8] fest, bei manchen Datenbanken kennt man die Feldlängen zur Compilezeit, der Numbercruncher könnte Probleme zerlegen bis vielleicht [256], um ein [256] mal [256] dann handoptimiert bereitzustellen, Filesystems haben germe mal [2048] oder so. Außerdem kann man öfters mal absturzfreie Zugriffe auf [256] garantieren, obwohl das dazu übergebene len == 0 sein könnte.

    Biolunar schrieb:

    restrict ist genau wie const : immer, wenn möglich.

    Wann nicht?



  • SeppJ schrieb:

    hustbaer schrieb:

    Ich hab's nicht ausprobiert, aber ich bin überzeugt dass restrict - bei bestimmten Funktionen, und wenn Inlining nicht möglich ist - schon viel bringt.

    Es ist nicht so, dass ich mir nicht vorstellen kann, dass es solche Fälle gibt, es ist, dass diese irgendwie nie vorkommen. Konstruiertes Beispiel eben. Wenn ich mal einen Grafikkartentreiber schreibe, mag das ganz anders aussehen, aber wer macht das schon? Meine Programme sind durch andere Probleme limitiert (...)

    Ja OK da wirst du Recht haben.
    In dem Code den die meisten von uns schreiben wird restrict vermutlich wenig bringen.
    Wenn man das ganze aber auf Libraries erweitert die von vielen verwendet werden, wäre ich mir schon nicht mehr so sicher.
    z.B. BLAS Libraries.


  • Mod

    Biolunar schrieb:

    leider ersetzt das nicht die Laufzeitprüfung auf NULL

    So wie das im Standard formuliert ist

    If the keyword static also appears within the [ and ] of the array type derivation, then for each call to the function, the value of the corresponding actual argument shall provide access to the first element of an array with at least as many elements as specified by the size expression.

    Resultiert allein der Aufruf mit NULL in UB.


Anmelden zum Antworten