Ich bin gerade dabei kritschen Interrupt-Code für einen Cortex M4 zu
optimieren und da fiel mir auf, dass GCC 11.2.0 (-Os) unterschiedlichen
Code für size_t/uint32_t erzeugt.
Das Disassembly im IRQ ist ein Pattern wo Loop-Unrolling forciert wird
sieht folgendermaßen aus:
1
// static size_t cnt_
2
// uint16_t ccr_
3
if(cnt_<ccr_){
4
8003b16:8812ldrhr2,[r2,#0]
5
8003b18:4293cmpr3,r2
6
8003b1a:f04f4290mov.wr2,#1207959552;0x48000000
7
8003b1e:bf34itecc
8
8003b20:2010movccr0,#16
9
8003b22:f44f1080movcs.wr0,#1048576;0x100000
10
8003b26:f8c20c18str.wr0,[r2,#3096];0xc18
Dieser Teil wird dabei etwa ein dutzend Mal wiederholt.
Ersetze ich den size_t Zähler durch einen uint32_t Zähler, dann erzeugt
der Compiler plötzlich ein zusätzliches Load für jeden
"Schleifendurchlauf".
1
// static uint32_t cnt_
2
// uint16_t ccr_
3
if(cnt_<ccr_){
4
8003b28:6819ldrr1,[r3,#0]
5
8003b2a:8812ldrhr2,[r2,#0]
6
8003b2c:4291cmpr1,r2
7
8003b2e:f04f4290mov.wr2,#1207959552;0x48000000
8
8003b32:bf34itecc
9
8003b34:2110movccr1,#16
10
8003b36:f44f1180movcs.wr1,#1048576;0x100000
11
8003b3a:f8c21c18str.wr1,[r2,#3096];0xc18
Ich versteh nicht so ganz welche Optimierung für size_t in Frage kommt
die für uint32_t nicht greift? Beide Typen sind unsigned integer mit 32
Bit Breite. Irgendwer eine Idee woran das liegen könnte?
Ich werf in der Zwischenzeit mal Clang an...
Programmierer schrieb:> Vincent H. schrieb:>> void setClear() const {>> if (cnt_ < ccr_)>> Ohne den ganzen Code und Definition von cnt_ und ccr_ kann man da nichts> zu sagen...
Was cnt_ und ccr_ is steht bereits oben.
Vincent H. schrieb:> Beide Typen sind unsigned integer mit 32> Bit Breite.
Es müssten sogar beide typedefs auf unsigned sein, also nicht nur das
gleiche, sondern das selbe.
Oliver
Oliver S. schrieb:> Vincent H. schrieb:>> Beide Typen sind unsigned integer mit 32>> Bit Breite.>> Es müssten sogar beide typedefs auf unsigned sein, also nicht nur das> gleiche, sondern das selbe.>> Oliver
Folgendes schlägt allerdings fehl
IMO riecht das stark nach Aliasing. Wenn "cnt_" und dein Counter beide
den selben Typ haben (size_t), wird der Compiler annehmen dass beide auf
der selben Speicherstelle liegen können. Versuche es mal mit
Vincent H. schrieb:> Oliver S. schrieb:>> Vincent H. schrieb:>>> Beide Typen sind unsigned integer mit 32>>> Bit Breite.>>>> Es müssten sogar beide typedefs auf unsigned sein, also nicht nur das>> gleiche, sondern das selbe.>>>> Oliver>> Folgendes schlägt allerdings fehl> static_assert(std::same_as<uint32_t, size_t>);
Dann drück halt auf die Taste deiner IDE, die dich zur jeweiligen
typedef- Deklaration bringt, und schau nach, was da steht.
Oliver
uint32_t entspricht long unsigned int
size_t entspricht unsigned int
_restrict_ brachte keinerlei Veränderung
Ich glaub eher dass für einen Typ irgendeine Optimierung im Register
Allocator greift und für den andern eben nicht... oder irgendwie sowas?
Ohne kompletten Code (reduziertes Minimalbeispiel) ist das alles
Kaffeesatzlesen. restrict ist etwas diffizil und muss richtig eingesetzt
werden.
Vincent H. schrieb:> Ich glaub eher dass für einen Typ irgendeine Optimierung im Register> Allocator greift und für den andern eben nicht... oder irgendwie sowas?
Nein, die Compiler behandeln "effektiv gleiche" Typen (wie long und int
wenn beide 32bit sind) im Backend (Optimierung) identisch (Pointer sind
aber natürlich nicht kompatibel).
Vincent H. schrieb:> Folgendes schlägt allerdings fehl> static_assert(std::same_as<uint32_t, size_t>);
arm-gcc 11.2 kennt godbolt noch nicht, aber mit arm-gcc 11.1 im 32-Bit
Mode hält das static_assert, im 64-Bit Mode nicht. Was allerdings so zu
erwarten war.
Oliver
Vincent H. schrieb:> uint32_t entspricht long unsigned int> size_t entspricht unsigned int
Selbst wenn das bei deinem gcc beides 32 bit unsigned Typen sind und
auch sein sollen (was man nachschauen müsste), sieht das doch seltsam
aus. Ich hätte das, wenn schon mit long, umgekehrt erwartet.
Oliver