Betrachten Sie die folgende Funktion:
void func(bool& flag)
{
if(!flag) flag=true;
}
Es scheint mir, dass wenn ein Flag einen gültigen booleschen Wert hat, dies einer bedingungslosen Einstellung true
wie folgt entspricht:
void func(bool& flag)
{
flag=true;
}
Weder gcc noch clang optimieren es auf diese Weise - beide generieren auf -O3
Optimierungsebene Folgendes :
_Z4funcRb:
.LFB0:
.cfi_startproc
cmp BYTE PTR [rdi], 0
jne .L1
mov BYTE PTR [rdi], 1
.L1:
rep ret
Meine Frage ist: Ist der Code nur zu speziell, um ihn zu optimieren, oder gibt es gute Gründe, warum eine solche Optimierung unerwünscht wäre, da dies flag
kein Verweis auf ist volatile
? Es scheint der einzige Grund zu sein, der sein könnte, dass er zum Zeitpunkt des Lesens flag
einen Nicht- true
oder- false
Wert ohne undefiniertes Verhalten haben könnte, aber ich bin mir nicht sicher, ob dies möglich ist.
c++
optimization
Ruslan
quelle
quelle
1
Verwendung. godbolt.org/g/swe0tcAntworten:
Dies kann sich aufgrund von Überlegungen zur Cache-Kohärenz negativ auf die Leistung des Programms auswirken . Das Schreiben in
flag
jedes Malfunc()
, wenn aufgerufen wird, würde die enthaltene Cache-Zeile verschmutzen. Dies geschieht unabhängig davon, dass der zu schreibende Wert genau mit den Bits übereinstimmt, die vor dem Schreiben an der Zieladresse gefunden wurden.BEARBEITEN
hvd hat einen weiteren guten Grund geliefert , der eine solche Optimierung verhindert. Es ist ein überzeugenderes Argument gegen die vorgeschlagene Optimierung, da es zu undefiniertem Verhalten führen kann, während meine (ursprüngliche) Antwort nur Leistungsaspekte betraf.
Nach etwas mehr Überlegung kann ich ein weiteres Beispiel vorschlagen, warum Compiler - sofern sie nicht nachweisen können, dass die Transformation für einen bestimmten Kontext sicher ist - von der Einführung des bedingungslosen Schreibens stark ausgeschlossen werden sollten. Betrachten Sie diesen Code:
Bei einem bedingungslosen Schreibvorgang
func()
wird definitiv ein undefiniertes Verhalten ausgelöst (das Schreiben in den Nur-Lese-Speicher beendet das Programm, selbst wenn der Effekt des Schreibvorgangs ansonsten ein No-Op wäre).quelle
const
einer Funktion , die die Daten ändern kann , die die Quelle des undefinierten Verhaltens sind, nicht das bedingungslose Schreiben. Doktor, es tut weh, wenn ich das mache ...Abgesehen von Leons Antwort auf die Leistung:
Angenommen,
flag
isttrue
. Angenommen, zwei Threads rufen ständig auffunc(flag)
. Die geschriebene Funktion speichert in diesem Fall nichts inflag
, daher sollte dies threadsicher sein. Zwei Threads greifen auf denselben Speicher zu, jedoch nur zum Lesen. Die bedingungslose Einstellungflag
auftrue
bedeutet, dass zwei verschiedene Threads in denselben Speicher schreiben. Dies ist nicht sicher, dies ist unsicher, selbst wenn die zu schreibenden Daten mit den bereits vorhandenen Daten identisch sind.quelle
[intro.races]/21
.0x01
in ein Byte bereits0x01
"unsicheres" Verhalten verursacht. Auf einem System mit Wort- oder Wortspeicherzugriff würde es; Der Optimierer sollte sich dessen jedoch bewusst sein. Auf einem modernen PC oder Telefonbetriebssystem tritt kein Problem auf. Das ist also kein triftiger Grund.flag
befindet sich auf einer Copy-on-Write-Seite. Jetzt kann auf CPU-Ebene das Verhalten definiert werden (Seitenfehler, lassen Sie das Betriebssystem damit umgehen), aber auf Betriebssystemebene ist es möglicherweise immer noch undefiniert, oder?Ich bin mir über das Verhalten von C ++ hier nicht sicher, aber in C kann sich der Speicher ändern, denn wenn der Speicher einen anderen Wert ungleich Null als 1 enthält, bleibt er bei der Prüfung unverändert, wird jedoch bei der Prüfung auf 1 geändert.
Da ich C ++ nicht sehr fließend beherrsche, weiß ich nicht, ob diese Situation überhaupt möglich ist.
quelle
_Bool
?bool
/ gültig ist_Bool
und Mittel isttrue
, dann haben Sie in diesem bestimmten ABI wahrscheinlich Recht._Bool
und C ++bool
entweder vom gleichen Typ oder kompatible Typen, die denselben Regeln folgen. Mit MSVC haben sie die gleiche Größe und Ausrichtung, aber es gibt keine offizielle Aussage darüber, ob sie die gleichen Regeln verwenden.<stdbool.h>
enthält eintypedef _Bool bool;
Und ja, auf x86 (zumindest im System V ABI) mussbool
/_Bool
entweder 0 oder 1 sein, wobei die oberen Bits des Bytes gelöscht sind. Ich halte diese Erklärung nicht für plausibel.func
in RDI übergeben wurde, während Windows RDX verwenden würde).