int vs. long (Cortex M4)



  • long scheint weniger effizient zu sein als int, obwohl beide 4 Byte groß sind (long produziert mehr code). Weiß jemand woran das liegen könnte?

    Grüße



  • Poste bitte mal Assemblercode des Compiler-Outputs. So kann man nur raten.

    Btw, int ist der native Datentyp der CPU. Den sollte man immer verwenden, wenn es keine handfesten Gründe für einen anderen Typ gibt. Das Schöne an C ist, dass es seine Länge nicht vorschreibt. IdR gekommt man bei Verwendung von "int" den kompaktesten und schnellsten Code.



  • Andromeda schrieb:

    Btw, int ist der native Datentyp der CPU.

    CPUs wie ARM haben keine Datentypen und schon gar nicht den einen "nativen". Es gibt Instruktionen, die verschieden schnell sein können. C ist aber kein Assembler. Solche Generalisierungen wie "immer int" funktionieren nicht.

    Andromeda schrieb:

    Den sollte man immer verwenden, wenn es keine handfesten Gründe für einen anderen Typ gibt.

    Genau das Gegenteil ist der Fall. Man sollte den Typ mit der passenden Größe verwenden. Selbst wenn man int (>= 16 bit signed) mal braucht, sollte man int_least16_t oder int_fast16_t verwenden, weil die Intention klar wird.

    Andromeda schrieb:

    Das Schöne an C ist, dass es seine Länge nicht vorschreibt. IdR gekommt man bei Verwendung von "int" den kompaktesten und schnellsten Code.

    Das ist eher das Hässliche an C, aber jeder wie er es mag. Was nützt mir der schnellste Code, wenn er schwer nachvollziehbar oder sogar falsch ist? Ich sehe int meistens da, wo size_t oder ptrdiff_t gemeint ist.

    fenrayn schrieb:

    long scheint weniger effizient zu sein als int, obwohl beide 4 Byte groß sind (long produziert mehr code). Weiß jemand woran das liegen könnte?

    Was ist das für ein Compiler? Es sollte identischer Code generiert werden.



  • Andromeda schrieb:

    Btw, int ist der native Datentyp der CPU.

    Nö.

    Wenn man tatsächlich so etwas wie von einem "nativen" Typen reden kann, dann wäre das uintptr_t , der die gleiche Länge wie ein Zeiger hat, aber nicht als Zeigertyp behandelt wird. Aber uintptr_t ändert sich, je nachdem, ob es sich bei dem Prozess um 16-, 32- oder 64-Bit handelt. Eher ist es also der "native" Typ des Modus, in dem sich die CPU gerade befindet.

    size_t != uintptr_t im Übrigen. Segmentierter Speicher kann dazu führen, dass size_t nur bis zur Größe des Segmentes geht. Deswegen macht man auch Spielereien auf Zeigeradressen (um z.B. auf Out-of-Bounds zu testen) so, dass man den Zeiger nach uintptr_t (nicht size_t ) konvertiert, dafür ist Overflow definiert, und dann kann man rechnen.

    TyRoXx schrieb:

    Das ist eher das Hässliche an C, aber jeder wie er es mag. Was nützt mir der schnellste Code, wenn er schwer nachvollziehbar oder sogar falsch ist? Ich sehe int meistens da, wo size_t oder ptrdiff_t gemeint ist.

    Stimme zu. Persönlich verwende ich die int_t -Typen - uint32_t , uint64_t ... da sieht man sofort, was gemeint ist.



  • dachschaden_off schrieb:

    Andromeda schrieb:

    Btw, int ist der native Datentyp der CPU.

    Nö.

    Wenn man tatsächlich so etwas wie von einem "nativen" Typen reden kann, dann wäre das uintptr_t , der die gleiche Länge wie ein Zeiger hat, aber nicht als Zeigertyp behandelt wird.

    Doch, schau hier: The type int should be the integer type that the target processor is most efficiently working with.
    --> https://en.wikipedia.org/wiki/C_data_types

    Also idealerweise entspricht "int" der Breite des Akkus oder sonstiger Register, mit denen die CPU rechnet (Datenbusbreite).

    Der uintptr_t ist eher für Adressen gedacht, da der Compiler streikt, wenn man mit void* zu rechnen versucht. Der C-Std schreibt dazu:

    The following type designates an unsigned integer type with the property that any valid
    pointer to void can be converted to this type, then converted back to pointer to void,
    and the result will compare equal to the original pointer:
    uintptr_t



  • TyRoXx schrieb:

    C ist aber kein Assembler.

    C ist eine abstrakte Assemblersprache für eine "abstrakte Maschine", siehe dazu auch "5.1.2.3 Program execution" im C-Standard. 🙂



  • Mit Code zeigen wird schwer, da ich nichtmal weiß, wo der zusätzliche Code dazu kommt ... Ich verwende für alle Größenangaben einen typedef, welcher zuvor immer als int32_t gewählt war (jaja signed int und Größenangaben, bitte nicht darüber nu streiten ... ^^). uint32_t hat den Typ long int. Hab den Typ mal auf int geändert, und mein erzeugter Code wird auf einmal kleiner ...

    Ich frage mich, gibt es bei der Cortex-M Reihe einen Unterschied zwischen int und long? Ich dachte es wäre auf der CPU identisch ...

    Als Compiler verwende ich den GNU Cross Compiler.



  • fenrayn schrieb:

    Mit Code zeigen wird schwer, da ich nichtmal weiß, wo der zusätzliche Code dazu kommt ... Ich verwende für alle Größenangaben einen typedef, welcher zuvor immer als int32_t gewählt war (jaja signed int und Größenangaben, bitte nicht darüber nu streiten ... ^^). uint32_t hat den Typ long int. Hab den Typ mal auf int geändert, und mein erzeugter Code wird auf einmal kleiner ...

    Probier mal "unsigned int" statt "int", dann bist du wieder bei der alten Größe, wetten?

    Ach ja, einfach uint32_t in "(signed) int" umzudefinieren ist gefährlich. Große Werte können dann plötzlich als negativ interpretiert werden und Vergleiche wie größer/kleiner und Shifts zicken plötzlich ganz seltsam rum.



  • Andromeda schrieb:

    Doch, schau hier: The type int should be the integer type that the target processor is most efficiently working with.
    --> https://en.wikipedia.org/wiki/C_data_types

    Die Effizienz hat nichts mit dem Typ zu tun, sondern mit den Operationen, die man darauf durchführt. Ein Prozessor könnte besonders schnell 32-bit signed addieren, aber bei Multiplikation ist 16-bit signed am schnellsten. Und nun? Ist int nun 16 oder 32-bit?

    Andromeda schrieb:

    Also idealerweise entspricht "int" der Breite des Akkus oder sonstiger Register, mit denen die CPU rechnet (Datenbusbreite).

    int ist auf aktuellen Architekturen kleiner als ein Register, weil int so oft als "don't care" 32-bit wird-schon-hinhauen-Typ missbraucht worden ist, dass Compiler ihn nicht mehr gefahrlos vergrößern können. int als bewusst gewählter Typ ist spätestens seit C99 überflüssig.

    Andromeda schrieb:

    TyRoXx schrieb:

    C ist aber kein Assembler.

    C ist eine abstrakte Assemblersprache für eine "abstrakte Maschine", siehe dazu auch "5.1.2.3 Program execution" im C-Standard. 🙂

    Welche Programmiersprache ist denn keine "Assemblersprache" für eine "abstrakte Maschine"?

    fenrayn schrieb:

    uint32_t hat den Typ long int. Hab den Typ mal auf int geändert, und mein erzeugter Code wird auf einmal kleiner ...

    Kann nicht sein, uint32_t ist unsigned.

    fenrayn schrieb:

    Als Compiler verwende ich den GNU Cross Compiler.

    Welche Version?



  • Andromeda schrieb:

    Doch, schau hier: The type int should be the integer type that the target processor is most efficiently working with.
    --> https://en.wikipedia.org/wiki/C_data_types

    Der C-Standard spezifiziert nur, dass mindestens 65536 unterschiedliche Stadien in einem int gespeichert werden kann, also mindestens zwei Bytes reserviert werden. Seit die Register 32 Bit groß werden, haben Compilerhersteller int s auf 4 Byte erweitert, das war es aber auch. Eine Erweiterung bei x64 fand nicht statt.

    Auf Windows bekommst du nicht einmal "native" Größe, wenn du long angibst - unsigned long auf 64-Bit Windows != size_t (guten Code erkennt man daran, dass er size_t für Längen verwendet). Und auch mit dem GCC bekommst du für normales int nur 4 Bytes - was lange nichts mit der nativen Größe zu tun hat.

    Andromeda schrieb:

    Der uintptr_t ist eher für Adressen gedacht, da der Compiler streikt, wenn man mit void* zu rechnen versucht.

    ... ja, das habe ich bereits geschrieben, aber dennoch Danke für die Paraphrasierung.



  • dachschaden_off schrieb:

    Der C-Standard spezifiziert nur, dass mindestens 65536 unterschiedliche Stadien in einem int gespeichert werden kann, also mindestens zwei Bytes reserviert werden. Seit die Register 32 Bit groß werden, haben Compilerhersteller int s auf 4 Byte erweitert, das war es aber auch. Eine Erweiterung bei x64 fand nicht statt.

    Wahrscheinlich sind 64 Bit breite Integers nur für sehr wenige Anwendungen sinnvoll. In den meisten Fällen schleppte man dann nur überflüssige 0-Bits mit sich rum.

    Ick kenne mich mit x86-Prozessoren nicht aus, aber ich wette, dass bei x64 die 32-bittigen Register und der dazugehörige Befehlssatz noch vollständig vorhanden sind. Und dass Code, der mit 32Bit-Integers auskommt, dort kompakter und schneller ist, als sein Äquivalent, der mit 64Bit-Ints arbeitet.



  • Sorry, hab mich verschrieben. Ich meinte natürlich int32_t hat den Typ long int. int_fas32_t. int_fast32_t hat den Typ int ... Sehr merkwürdig.

    Ja, unsigned int und long scheinen gleicheffektiv zu sein. aber int scheint effektiver als long und unsigned int zu sein. Undinged long ist wieder weniger effektiv als long und unsigned int. Aber das mit dem Unsigned kann ja nu auch nur zufall sein.



  • TyRoXx schrieb:

    Die Effizienz hat nichts mit dem Typ zu tun, sondern mit den Operationen, die man darauf durchführt. Ein Prozessor könnte besonders schnell 32-bit signed addieren, aber bei Multiplikation ist 16-bit signed am schnellsten. Und nun? Ist int nun 16 oder 32-bit?

    Ein 16-Bitter arbeitet mit 16-bittigen Operanden am besten/schnellsten, und ein 32-Bitter mit 32-bittigen Operanden, weil die Hardware darauf ausgelegt ist, die entsprechenden n Bits parallel zu verarbeiten.

    Demzufolge sollte eine 32-bit CPU auch auf recht effizient Weise einen 32bit*32bit mult verarbeiten, wenn sie nicht gerade aus der FPGA-Hobby-Bastelküche des kleinen Computerfreaks von nebenan kommt. 🙂



  • Andromeda schrieb:

    dachschaden_off schrieb:

    Der C-Standard spezifiziert nur, dass mindestens 65536 unterschiedliche Stadien in einem int gespeichert werden kann, also mindestens zwei Bytes reserviert werden. Seit die Register 32 Bit groß werden, haben Compilerhersteller int s auf 4 Byte erweitert, das war es aber auch. Eine Erweiterung bei x64 fand nicht statt.

    Wahrscheinlich sind 64 Bit breite Integers nur für sehr wenige Anwendungen sinnvoll. In den meisten Fällen schleppte man dann nur überflüssige 0-Bits mit sich rum.

    64-bit Integer sind auch in 32-bit-Programmen zwingend notwendig, zum Beispiel für Dateigrößen oder Zeitmessungen. In 64-bit-Programmen müssen in der Regel Array-Indizes, Objektgrößen und Zahlen von Objekten in 64-bit gerechnet werden. Es gibt kaum Fälle, wo int (>= 16-bit signed!) mal nützlich ist.

    Nun kann man argumentieren, dass man nur Compiler verwendet, bei denen int genau 32-bit hat, oder man macht es endlich einheitlich und schreibt int32_t , wenn man es meint.

    Andromeda schrieb:

    Ick kenne mich mit x86-Prozessoren nicht aus, aber ich wette, dass bei x64 die 32-bittigen Register und der dazugehörige Befehlssatz noch vollständig vorhanden sind.

    Ja, wobei die 32-bit-Register einfach eine Hälfte der 64-bit-Register sind.

    Andromeda schrieb:

    Und dass Code, der mit 32Bit-Integers auskommt, dort kompakter und schneller ist, als sein Äquivalent, der mit 64Bit-Ints arbeitet.

    Das macht in der Praxis zwar kaum einen Unterschied, aber dafür gibt es int32_fast_t und so.

    Mal ganz abgesehen davon, dass Integer in C furchtbar spezifiziert sind wegen all des undefinierten, implementationsabhängigen und überraschenden Verhaltens. Außerdem fehlen wichtige Operationen zum Erkennen von Overflows. Gerade wegen dieser Gefahren halte ich es für besonders wichtig ein Minimum an hilfreichen Praktiken umzusetzen anstatt sich Cargo-Cults und seltsamen "Performance"-Argumenten hinzugeben.



  • TyRoXx schrieb:

    Nun kann man argumentieren, dass man nur Compiler verwendet, bei denen int genau 32-bit hat, oder man macht es endlich einheitlich und schreibt int32_t , wenn man es meint.

    Kann man machen, aber damit verwirft man quasi die Kompetenz des Compilerbauers, der effizienteste Codegenerierung bei "int" garantiert, während zur Spezialbehandlung von unsigned- und größeren/kleineren Datentypen irgendwelche Maskierungen und sonstige Konvertierungen benötigt werden, die unnötig Processor-Cycles fressen.

    TyRoXx schrieb:

    Mal ganz abgesehen davon, dass Integer in C furchtbar spezifiziert sind wegen all des undefinierten, implementationsabhängigen und überraschenden Verhaltens. Außerdem fehlen wichtige Operationen zum Erkennen von Overflows. Gerade wegen dieser Gefahren halte ich es für besonders wichtig ein Minimum an hilfreichen Praktiken umzusetzen anstatt sich Cargo-Cults und seltsamen "Performance"-Argumenten hinzugeben.

    "Trust the programmer" und
    "Make it fast, even if it is not guaranteed to be portable"
    sind Kernpunkte des Sprit of C.
    ... und darum liebe ich C. 🙂

    Wer C zu unspezifiziert findet, kann sich doch anderen Sprachen zuwenden.
    Ich find zum Beispiel Java auch ganz gut, obwohl das Typsystem dort viel strenger ist.


  • Mod

    fenrayn schrieb:

    Ja, unsigned int und long scheinen gleicheffektiv zu sein. aber int scheint effektiver als long und unsigned int zu sein. Undinged long ist wieder weniger effektiv als long und unsigned int. Aber das mit dem Unsigned kann ja nu auch nur zufall sein.

    Welches Datenmodel (ILP32, LP64, LLP64) ? Bei ILP32 glaube ich das nicht ohne Belege.



  • Da long und int beide 32 Bit lang sind vermute ich ILP32.


Log in to reply