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::rand gemacht. Zu meiner Überraschung können Compiler das nicht optimieren. Weder GCC noch Clang erkennt das Muster und generiert bswap . htonl wird aber durch bswap ersetzt.

    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	_ZNSo5writeEPKci
    

    Wieder etwas gelernt. Hier sollte man dem Compiler dabei helfen optimalen Code zu generieren. Wenn htonl zur Verfügung steht, kann man es benutzen, um Premature Pessimization zu vermeiden. Die Variante mit der Schleife kann dann als Fallback dienen und kommt bei long long zum 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 long z.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 swap oder 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 htonl implementiert 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 wie htons in 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 long z.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?


  • Mod

    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 swap oder 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.


Anmelden zum Antworten