Der Teil des Gehirns, der für Byte Order zuständig ist
-
punner schrieb:
2. Strict Aliasing wird verletzt, UB
Nö. Lies 3.10/10 noch mal genau.
-
Ich habe das noch einmal für Werte aus
std::randgemacht. Zu meiner Überraschung können Compiler das nicht optimieren. Weder GCC noch Clang erkennt das Muster und generiertbswap.htonlwird aber durchbswapersetzt.Clang 3.2 generiert folgendes:
calll rand movl %eax, %ebx shrl $24, %eax movb %al, 296(%esp) movl %ebx, %eax shrl $16, %eax movb %al, 297(%esp) movb %bh, 298(%esp) movb %bl, 299(%esp) leal 296(%esp), %eax movl %eax, 4(%esp) movl %edi, (%esp) movl $4, 8(%esp) calll _ZNSo5writeEPKci bswapl %ebx movl %ebx, 300(%esp) leal 300(%esp), %eax movl %eax, 4(%esp) movl %edi, (%esp) movl $4, 8(%esp) calll _ZNSo5writeEPKciWieder etwas gelernt. Hier sollte man dem Compiler dabei helfen optimalen Code zu generieren. Wenn
htonlzur Verfügung steht, kann man es benutzen, um Premature Pessimization zu vermeiden. Die Variante mit der Schleife kann dann als Fallback dienen und kommt beilong longzum Einsatz. Wenn man ganz sicher weiß, dass die native Byte Order Big Endian ist, kann man die Schleife weglassen, um den Compiler nicht zu verwirren.#include <cstdint> #include <ostream> #include <climits> #include <fstream> #include <cstdlib> #define BIG_ENDIAN_HOST_DETECTED 0 #define HTONL_DETECTED 1 #include <netinet/in.h> template <class T> struct is_non_bool_integral : std::is_integral<T> { }; template <> struct is_non_bool_integral<bool> : std::false_type { }; template <class Integer> typename std::enable_if<is_non_bool_integral<typename std::decay<Integer>::type>::value, void>::type encode_big_endian_compromise(std::ostream &out, Integer value) { Integer buffer; switch (sizeof(value)) { #if HTONL_DETECTED case 2: buffer = htons(value); break; case 4: buffer = htonl(value); break; #endif default: { #if !BIG_ENDIAN_HOST_DETECTED typedef typename std::make_unsigned<Integer>::type Unsigned; Unsigned const shiftableValue = value; unsigned char * const digits = reinterpret_cast<unsigned char *>(&buffer); for (std::size_t i = 0; i < sizeof(buffer); ++i) { digits[i] = static_cast<unsigned char>( shiftableValue >> ((sizeof(buffer) - i - 1) * CHAR_BIT) ); } #endif break; } } out.write(reinterpret_cast<char *>(&buffer), sizeof(buffer)); } int main() { std::ofstream file("out.bin", std::ios::binary); if (!file) { return 1; } std::uint32_t const i = std::rand(); encode_big_endian_compromise(file, i); }punner schrieb:
1. Funktioniert nicht mit bool
Soll es auch nicht.
Danke für die Antworten, die konstruktiv sind.
-
Danke für die Antworten, die konstruktiv sind.
Danke, dass du auf Kritikpunkte nicht eingegangen bist. Danke, dass dein Code noch komplexer geworden ist. Danke, fuer eine inperformante Loesung eines gut untersuchten und mittlerweile voellig trivialen Problems. Danke, dass es an streams gekoppelt ist. Danke, dass es nicht fuer Arrays funktioniert. Danke fuer all die Template-Magic und die Makros. Danke dafuer, dass du dir keine Anregungen
long longz.B. hier geholt hast.
-
kann jemand Zeile 120-133 Standardkonform und portabel aufschreiben?
Dann hat ja knivil recht und der Thread kann geschlossen werden.
-
cooky451 schrieb:
TyRoXx schrieb:
Ich will gar keine Bytes tauschen. Niemand sollte das wollen, darum geht es hier doch.
Versteh ich nicht.. macht encode_big_endian_correct_impl nicht genau das, bytes tauschen?
Nein, dann würde das Template
swapoder so im Namen haben. Es soll nur Zahlen kodieren (High Level) und die konkrete Implementation auf der Plattform interessiert nicht (Low Level). Auf einer Big-Endian-Plattform würde nicht einmal etwas vertauscht werden.Die "falsche" Variante besteht aus zwei Schritten: Zahl vorbereiten (
htonl), vorbereitete Zahl als POD wegschreiben. (Im ersten Beispiel-Code habe ich die zwei Schritte zusammengefasst, aber typischerweise machen die Leute nicht einmal das.)
Die "richtige" ist nur ein Schritt: Zahl im richtigen Format wegschreiben.Ob die zweite Variante mit
htonlimplementiert ist, interessiert dabei nicht. Die Kodierung ist genau ein mal in einem Header implementiert und kann idiotensicher benutzt werden. Der Anwender kommt nicht mit plattformabhängigen Funktionen wiehtonsin Berührung. Er profitiert aber trotzdem von den Optimierungen.knivil schrieb:
Danke, dass du auf Kritikpunkte nicht eingegangen bist.
Worauf soll ich denn noch eingehen?
knivil schrieb:
Danke, dass dein Code noch komplexer geworden ist. [...]Danke, dass es an streams gekoppelt ist. Danke, dass es nicht fuer Arrays funktioniert. Danke fuer all die Template-Magic und die Makros.
Tut mir leid, dass du nicht mehr mitkommst. Irgendwo im Forum gibt es einen Thread mit guten Büchern über C++.
knivil schrieb:
Danke, fuer eine inperformante Loesung eines gut untersuchten und mittlerweile voellig trivialen Problems.
Hast du eigentlich mein letztes Programm verstanden? Was soll da noch performanter gehen, ich verwende doch schon
htonl. Ich habe gemerkt, dass Compiler damit besser arbeiten können und meinen Code entsprechend angepasst.knivil schrieb:
Danke dafuer, dass du dir keine Anregungen
long longz.B. hier geholt hast.Intrinsics für
long long. Wow, High Tech. Das wäre jetzt voll wichtig gewesen für meine Argumentation. Reicht ja nicht, dass mein Programm schon für 16 und 32 Bit optimal ist und nur zur Demonstration des Prinzips dienen soll.otze schrieb:
kann jemand Zeile 120-133 Standardkonform und portabel aufschreiben?
Dann hat ja knivil recht und der Thread kann geschlossen werden.Genau, schließt alle interessanten Diskussionen!
-
1.) Der union-Trick funktioniert auf allen mir bekannten Compilern/Architekturen (zugegeben, sind nicht viel). Vielleicht sollte der Standard diesbezueglich nachgeruestet werden.
2.) Zeile 100-108 sieht auch gut aus. Man kann gern noch etwas make unsigned rumspielen.
-
knivil schrieb:
1.) Der union-Trick funktioniert auf allen mir bekannten Compilern/Architekturen (zugegeben, sind nicht viel). Vielleicht sollte der Standard diesbezueglich nachgeruestet werden.
Ohh, you think darkness is your ally. ~
knivil schrieb:
Man kann gern noch etwas make unsigned rumspielen.
Das ist nicht "rumspielen", sondern es gibt dem Shift definiertes Verhalten.
-
TyRoXx schrieb:
Das ist nicht "rumspielen", sondern es gibt dem Shift definiertes Verhalten.
Genau das meine ich. Du gehst nicht auf das eigentliche Problem ein. Ich weiss das shift nur fuer unsigned definiert ist. Aber: Was ist an Zeile 100-108 jetzt schlecht, bis auf das unsigned oder Macros?
-
knivil schrieb:
Was ist an Zeile 100-108 jetzt schlecht, bis auf das unsigned oder Macros?
Zeile 100-108 von http://repo-genesis3.cbi.utsa.edu/crossref/ns-sli/usr/include/bits/byteswap.h.html:
# define __bswap_constant_64(x) \ ((((x) & 0xff00000000000000ull) >> 56) \ | (((x) & 0x00ff000000000000ull) >> 40) \ | (((x) & 0x0000ff0000000000ull) >> 24) \ | (((x) & 0x000000ff00000000ull) >> 8) \ | (((x) & 0x00000000ff000000ull) << 8) \ | (((x) & 0x0000000000ff0000ull) << 24) \ | (((x) & 0x000000000000ff00ull) << 40) \ | (((x) & 0x00000000000000ffull) << 56))Meinst du das ernst?
-
Oh, wir spielen uns also mit Pseudofragen zu, damit wir uns dann nachher gegenseitig auslachen koennen?
-
Ich verstehe nicht, welches Problem in diesem Thread gelöst werden soll.
-
Portable performante Loesung fuer byteswap.
-
TyRoXx schrieb:
Nein, dann würde das Template
swapoder so im Namen haben. Es soll nur Zahlen kodieren (High Level) und die konkrete Implementation auf der Plattform interessiert nicht (Low Level). Auf einer Big-Endian-Plattform würde nicht einmal etwas vertauscht werden.Das heißt das einzige was du jetzt anders gemacht hast ist das swappen und das write in eine gemeinsame Funktion zu stecken? Oder wie? Scheint mir nicht besonders neu zu sein.. aber auch nicht besonders nützlich ohne dass man swap auch so in einer Funktion hat, denn man braucht so etwas eben nicht immer zusammen mit write.