endianess detection and performance in C

风流意气都作罢 提交于 2019-12-23 05:41:44

问题


I've got a performance critical C code that needs to work on a variety of platform. Some of them are little endian, others are big endian.

Detecting endianess is currently an imperfect process, based on macro detection. But it's difficult to be sure that the macro detection will work for all combinations of systems and compilers. Welcome to the world of portable code.

One relatively safe way to detect endianess is to use a runtime test, and hope that it will get optimized out by the compiler. Something along these lines :

static const int one = 1;
#define IS_LITTLE_ENDIAN (*(char*)(&one))

In general it works. The compiler should properly detect that the result of this macro is always the same for a given architecture (1 for little endian, 0 for big endian), and simply remove the memory access and associated branch altogether.

My question is : is it always the case ? Can we expect the compiler to always properly understand this test, and always optimize it correctly ? (assuming -O2/-O3 or equivalent optimization level, not applicable of course to debug code)

I'm especially worried for bi-endian CPU, like for example ARM. Since such CPU can be either big endian or little endian, depending on OS parameters, it might be difficult for the compiler to "hardwire" such endian test. On the other hand, I don't expect an application to work in "either endian mode of choice" : I guess it should be compiled for one precise and definitive endianess. Therefore, IS_LITTLE_ENDIAN should always result the same.

Anyway, I'm asking for experience of people having met such situation. Since I don't have a bi-endian CPU and compiler to play with currently, I'm not in position to test and observe above assumption.

[Edit] @Brandin proposes to "save the result" of the macro, making it a variable. I guess he proposes something like this :

static const int one = 1;
static const int isLittleEndian = *(char*)(&one);

Since a static const int is evaluated at compile time, it would indeed guarantee that the compiler is necessarily aware of the value of isLittleEndian, and can therefore properly optimize branches which use this variable.

Unfortunately, it doesn't work. The above declaration result in the following compilation error :

error: initializer element is not constant

I guess that's because &one (a pointer address) cannot be evaluated at compile time.

@HuStmpHrrr's variant, using union instead, looks better : there is no pointer address to evaluate. Unfortunately, it doesn't work better, and results in the same compilation error.

I guess that's because unions are not considered simple enough by the compiler to be usable as a value for a static const initialization.

So we're back to the beginning, with a macro instead.


回答1:


The same idea but different trick. this code will work too.

union {
  int num;
  char[sizeof(int)] bytes;
} endian;

endian.num = 1;

then use endian.bytes[0] to judge.

This way, things come more naturally and compiler should be expected to do something, since this is so easy to be tracked by a simple data flow optimizer implementation.

endian.bytes[0] should be shrunken down to a constant.

Anyway, this way is compiler dependent.




回答2:


Ignoring the minor issue I have with the post linked inthe comment by @OliCharlesworth, I agree with its spirit: are you sure the endianness will really matter, and if it does, why?

  • If it doesn't actually matter, the portable and probably very fast solution is to use memcpy, which will copy the bytes in host byte order. This means files won't be interchangeable between big and little endian machines.

  • You can convert an integer to a specific endianness if needed as suggested in the aforementioned post by performing bit-shift operations on values regardless of endianness. For example, writing (value >> 8) & 0xff followed by (value >> 0) & 0xff to a file instead of the raw value itself will write a 16-bit value in big endian order always, and you will always read the upper 8 bits (most significant byte) of the 16-bit value first. This is essentially what functions like htonl do, except the value returned by the function may be different due to the possibility of the restructuring of its underlying byte representation, which is different from simply writing shifted bytes one by one.

Without more context other than "I have performance-critical code", any other guidance is unavailable. The primary issue, which I've already mentioned, is the reason why a machine's endianness is so crucial to this problem.




回答3:


You can use htonl to detect it :

#include <stdio.h>
int main()
{
        if (htonl(1) == 1)
                printf("big endian\n");
        else
                printf("little endian\n");
}


来源:https://stackoverflow.com/questions/25391730/endianess-detection-and-performance-in-c

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!