Führt Typumwandlung in C/C++ zu zusätzlichen CPU-Zyklen?
Mein Verständnis ist, dass es zumindest in bestimmten Fällen zusätzliche CPU-Zyklen verbrauchen sollte. Wie Typumwandlung von Float in Integer, wo die CPU eine Float-Struktur in Integer umwandeln sollte.
float a=2.0;
int b= (float)a;
Ich würde gerne die Fälle verstehen, in denen es zusätzliche CPU-Zyklen verbrauchen würde/nicht.
Es kommt auf die Art der Besetzung an.
– JoG
14. Mai 2013 um 9:23 Uhr
Da Sie dies mit C++ gekennzeichnet haben, würde ich die Verwendung von Cast im C++-Stil vorschlagen, nämlich static_cast<T>, dynamic_cast<T>, reinterpret_cast<T> und const_cast<T>.
– JBL
14. Mai 2013 um 9:25 Uhr
Selbst abgesehen von allen sprachbezogenen Details und Kleinigkeiten ist es wahrscheinlich stark plattformabhängig, ob eine Besetzung “kostenlos” ist oder nicht. Beispielsweise sind auf x86 die meisten Integer-Umwandlungen von größeren zu kleineren Typen kostenlos, aber dies ist auf einer anderen Plattform mit anderen Eigenschaften möglicherweise nicht der Fall.
– yzt
14. Mai 2013 um 9:29 Uhr
+1 Gute Frage, @JoeGauterin tut es (size_t)int (wird normalerweise für Malloc beim Schleifen verwendet) zusätzliche Zyklen verbrauchen?
– David Ranieri
14. Mai 2013 um 9:45 Uhr
@ DavidRF: Hängt davon ab wie size_t und int auf Ihrer lokalen Plattform definiert sind und wie gut der Optimierer auf Ihrem Compiler ist. Überprüfen Sie im Zweifelsfall die Montage.
– Jack Aidley
14. Mai 2013 um 10:59 Uhr
Ich möchte sagen, dass wir uns mit der „Umwandlung zwischen Typen“ befassen sollten, nicht ob es eine Besetzung gibt oder nicht. Zum Beispiel
int a = 10;
float b = a;
wird dasselbe sein wie:
int a = 10;
float b = (float)a;
Dies gilt auch für die Größenänderung einer Schrift, z
char c="a";
int b = c;
dies wird “erweitern c In ein int Größe von einem einzelnen Byte [using byte in the C sense, not 8-bit sense]”, wodurch möglicherweise eine zusätzliche Anweisung (oder zusätzliche Taktzyklen zu der verwendeten Anweisung) über die Datenbewegung selbst hinaus hinzugefügt wird.
Beachten Sie, dass diese Konvertierungen manchmal überhaupt nicht offensichtlich sind. Auf x86-64 wird ein typisches Beispiel verwendet int Anstatt von unsigned int für Indizes in Arrays. Da Zeiger 64-Bit sind, muss der Index in 64-Bit konvertiert werden. Im Falle eines vorzeichenlosen ist das trivial – verwenden Sie einfach die 64-Bit-Version des Registers, in dem sich der Wert bereits befindet, da eine 32-Bit-Ladeoperation den oberen Teil des Registers mit Nullen füllt. Aber wenn du eine hast int, es könnte negativ sein. Der Compiler muss also die Anweisung “Sign Extend this to 64 Bits” verwenden. Dies ist normalerweise kein Problem, wenn der Index basierend auf einer festen Schleife berechnet wird und alle Werte positiv sind, aber wenn Sie eine Funktion aufrufen, bei der nicht klar ist, ob der Parameter positiv oder negativ ist, muss der Compiler den Wert definitiv erweitern . Ebenso, wenn eine Funktion einen Wert zurückgibt, der als Index verwendet wird.
Allerdings wird kein einigermaßen kompetenter Compiler gedankenlos Anweisungen hinzufügen, um etwas von seinem eigenen Typ in sich selbst zu konvertieren (möglicherweise, wenn die Optimierung deaktiviert ist, kann dies der Fall sein – aber minimale Optimierung sollte sehen, dass “wir von Typ X in Typ X konvertieren, das hat nichts zu bedeuten, nehmen wir es weg”).
Kurz gesagt, das obige Beispiel fügt keine zusätzliche Strafe hinzu, aber es gibt sicherlich Fälle, in denen “das Konvertieren von Daten von einem Typ in einen anderen dem Code zusätzliche Anweisungen und / oder Taktzyklen hinzufügt”.
Es spielt keine Rolle, die Größe zu ändern, da es in einem Allzweckregister Aliase für Teile des Registers gibt … natürlich könnte es … aber selbst auf dem Stapel können Sie aufgrund von Ausrichtungen normalerweise einfach a greifen größere Version eines vorzeichenlosen Werts.
– Grady-Spieler
14. Mai 2013 um 16:09 Uhr
Es gibt zwei Probleme beim Vergrößern eines Werts: 1. Was steht im “oberen Teil”, 2. was im oberen Teil sein SOLLTE. Vorzeichenlose Werte funktionieren auf x86-64 konstruktionsbedingt, da der obere Teil des Registers mit Nullen gefüllt ist. Aber wenn das Register vorzeichenerweitert werden soll, muss es mit dem Vorzeichen gefüllt werden, was bedeutet, dass eine zusätzliche Anweisung erforderlich ist. Natürlich wird es wahrscheinlich nicht funktionieren, 64-Bit vom Stack zu lesen, da es absolut keine Garantie dafür gibt, dass die oberen 32 Bit tatsächlich den gewünschten Wert haben.
– Mats Petersson
14. Mai 2013 um 16:14 Uhr
Jack Aidley
Es verbraucht Zyklen, wo es die zugrunde liegende Darstellung ändert. Es verbraucht also Zyklen, wenn Sie a konvertieren float zu einem int oder umgekehrt. Je nach Architektur wirft wie z int zu char oder long long zu int kann Zyklen verbrauchen oder auch nicht (aber meistens werden sie es tun). Das Umwandeln zwischen Zeigertypen verbraucht nur Zyklen, wenn Mehrfachvererbung beteiligt ist.
Nur ein kleiner Kommentar. long long to int sollte nur dann keine Zyklen verbrauchen, wenn sizeof(long long)==sizeof(int).
– Vishal
14. Mai 2013 um 9:35 Uhr
@Vishal Das ist eigentlich nicht wahr, da Sie mit einigen Architekturen ein Register bei der Verwendung als 32-Bit oder 64-Bit neu interpretieren können. ARM ist ein solches Beispiel. Der Gips muss normalerweise nicht separat durchgeführt werden.
– Jack Aidley
25. Mai 2019 um 9:26 Uhr
Es gibt verschiedene Arten von Abgüssen. C++ hat verschiedene Arten von Umwandlungsoperatoren für die verschiedenen Arten von Umwandlungen. Wenn wir es so betrachten, …
static_cast ist normalerweise mit Kosten verbunden, wenn Sie von einem Typ in einen anderen konvertieren, insbesondere wenn der Zieltyp eine andere Größe als der Quelltyp hat. static_casts werden manchmal verwendet, um einen Zeiger von einem abgeleiteten Typ auf einen Basistyp umzuwandeln. Dies kann auch Kosten verursachen, insbesondere wenn die abgeleitete Klasse mehrere Basen hat.
reinterpret_cast werden in der Regel keine direkten Kosten verursachen. Grob gesagt ändert diese Art der Umwandlung den Wert nicht, sie ändert nur, wie er interpretiert wird. Beachten Sie jedoch, dass dies indirekte Kosten verursachen kann. Wenn Sie einen Zeiger auf ein Array von Bytes als Zeiger auf ein int neu interpretieren, zahlen Sie möglicherweise jedes Mal Kosten, wenn Sie diesen Zeiger dereferenzieren, es sei denn, der Zeiger ist wie von der Plattform erwartet ausgerichtet.
const_cast sollte nichts kosten, wenn Sie constness hinzufügen oder entfernen, da es sich hauptsächlich um eine Anmerkung zum Compiler handelt. Wenn Sie es verwenden, um einen flüchtigen Qualifizierer hinzuzufügen oder zu entfernen, kann es einen Leistungsunterschied geben, da dadurch bestimmte Optimierungen aktiviert oder deaktiviert würden.
dynamic_castdas verwendet wird, um von einem Zeiger auf eine Basisklasse auf einen Zeiger auf eine abgeleitete Klasse umzuwandeln, ist mit Sicherheit mit Kosten verbunden, da es zumindest prüfen muss, ob die Konvertierung angemessen ist.
Wenn Sie eine herkömmliche C-Umwandlung verwenden, bitten Sie den Compiler im Wesentlichen nur, die spezifischere Art der Umwandlung auszuwählen. Um also herauszufinden, ob Ihr C-Cast etwas kostet, müssen Sie herausfinden, um welche Art von Cast es sich wirklich handelt.
Ja, es müssen wirklich 2 Fragen sein, eine für C-Code, was normalerweise eine Unterscheidung auf Assembly-Ebene und C++-Code ist, der am Ende Initialisierer und Vorlagencode ausführen könnte und 1000 Operationen für einen Cast sein könnte, der einfach genug aussieht und ist automatisch aus der Perspektive des Programmierers.
Es kommt auf die Art der Besetzung an.
– JoG
14. Mai 2013 um 9:23 Uhr
Da Sie dies mit C++ gekennzeichnet haben, würde ich die Verwendung von Cast im C++-Stil vorschlagen, nämlich
static_cast<T>
,dynamic_cast<T>
,reinterpret_cast<T>
undconst_cast<T>
.– JBL
14. Mai 2013 um 9:25 Uhr
Selbst abgesehen von allen sprachbezogenen Details und Kleinigkeiten ist es wahrscheinlich stark plattformabhängig, ob eine Besetzung “kostenlos” ist oder nicht. Beispielsweise sind auf x86 die meisten Integer-Umwandlungen von größeren zu kleineren Typen kostenlos, aber dies ist auf einer anderen Plattform mit anderen Eigenschaften möglicherweise nicht der Fall.
– yzt
14. Mai 2013 um 9:29 Uhr
+1 Gute Frage, @JoeGauterin tut es
(size_t)int
(wird normalerweise für Malloc beim Schleifen verwendet) zusätzliche Zyklen verbrauchen?– David Ranieri
14. Mai 2013 um 9:45 Uhr
@ DavidRF: Hängt davon ab wie
size_t
undint
auf Ihrer lokalen Plattform definiert sind und wie gut der Optimierer auf Ihrem Compiler ist. Überprüfen Sie im Zweifelsfall die Montage.– Jack Aidley
14. Mai 2013 um 10:59 Uhr