考虑以下要转换为单个无符号整数的字节数组:
unsigned char arr[3] = {0x23, 0x45, 0x67};
每个字节代表整数的等效字节,现在你会特别建议以下一种方法:
unsigned int val1 = arr[2] << 16 | arr[1] << 8 | arr[0];
//or
unsigned int val2=arr[0];
*((char *)&val2+1)=arr[1];
*((char *)&val2+2)=arr[2];
答案 0 :(得分:6)
我更喜欢第一种方法,因为它是便携式的。第二个问题不是endianness问题。
答案 1 :(得分:4)
第一个更快,在x86 asm中翻译。这取决于你的架构。通常编译器能够非常好地优化第一个表达式,并且它也更加便携
答案 2 :(得分:4)
这取决于您的特定处理器,很多。
例如,在PowerPC上,第二种形式 - 通过字符指针编写 - 会遇到一个棘手的实现细节,称为 load-hit-store 。这是当您存储到内存中的某个位置时发生的CPU停顿,然后在存储完成之前再次读取它。在存储完成之前,加载操作无法完成(大多数PPC没有内存存储转发),并且存储可能需要很多周期才能从CPU输出到内存缓存。
由于存储和算术单元在流水线中的排列方式,CPU必须完全冲洗流水线直到存储完成:这可能是一个20个周期或更长时间的停顿,在此期间CPU已经停止运行。一般来说,写入内存然后立即回读它在这个平台上是非常糟糕的。因此在这种情况下,顺序位移将更快,因为它们都发生在寄存器上,并且不会导致流水线停顿。
在Pentium系列上,情况可能完全颠倒,因为芯片组 具有存储转发和快速堆栈架构,以及相对较少的架构寄存器。在Core Duos和i7s上,它可能会再次反转,因为它们的管道非常深。
请记住:并非每个操作码需要一个周期。 CPU并不简单,superscalar pipes和data hazards之类的操作可能会导致指令占用很多周期,或者甚至是每个周期发生的许多指令,具体取决于您如何安排代码。
所有这一切只是为了强调这一点:这种优化非常特定于特定的编译器和芯片组。所以你必须编译,测试和测量。
答案 3 :(得分:2)
性能取决于编译器和机器。例如,在我使用g64 4.4.5在x64上进行的实验中,第二个稍快一些,而其他人报告第一个更快。因此,我建议坚持使用第一个,因为它更干净(没有强制转换)并且更安全(没有字节序问题)。
答案 4 :(得分:1)
我相信bitshift将是最快的解决方案。在我看来,CPU可以只滑动值,但直接转到地址,就像你的第二个例子一样,它将不得不使用许多临时存储。
答案 5 :(得分:1)
我建议使用union解决方案:
union color {
// first representation (member of union)
struct s_color {
unsigned char a, b, g, r;
} uc_color;
// second representation (member of union)
unsigned int int_color;
};
int main()
{
color a;
a.int_color = 0x23567899;
a.uc_color.a;
a.uc_color.b;
a.uc_color.g;
a.uc_color.r;
}
注意它依赖于平台(哪个endianess)