复制过程中的字节交换

8

我需要在将一个数组复制到另一个数组时高效地交换字节顺序。

源数组是某种类型的; char,short或int,因此所需的字节交换是明确的,并且将根据该类型进行。

我的计划是使用多遍逐字节复制(2个用于short,4个用于int等)来实现这一点。但是是否有任何现有的“memcpy_swap_16/32/64”函数或库?也许在BGR / RGB图像处理中存在。

编辑

我知道如何交换单个值的字节,这不是问题。我想在我将要执行的复制过程中完成此过程。

例如,如果我有一个小端4字节整数数组,我可以通过执行初始偏移量为0、1、2和3,步幅为4的4次逐字节复制来完成它们的交换。但可能有更好的方法,甚至读取每个4字节整数并使用字节交换内置函数_byteswap_ushort,_byteswap_ulong和_byteswap_uint64会更快。但我怀疑必须存在执行此类处理的现有函数。

编辑2

我刚刚发现了这个链接,它可能是SSE的有用基础,尽管内存带宽可能使其浪费时间。

快速矢量化从RGB到BGRA的转换

3个回答

6

Unix系统有一个swab函数,可以对16位数组进行字节交换。它可能已经被优化了,但我不确定。请注意,如果你只是编写简单的字节交换代码,现代gcc会生成极其高效的代码:

uint32_t x, y;
y = (x<<24) | (x<<8 & 0xff0000) | (x>>8 & 0xff00) | (x>>24);

即,它将在i486+上使用bswap指令。假设将其放入循环中也会给出一个有效的循环...
编辑:对于您的复制任务,在您的循环中,我会执行以下操作:
1. 从const uint32_t *src读取一个32位值。 2. 使用上述代码进行交换。 3. 将一个32位值写入uint32_t *dest
严格来说,这可能不可移植(别名违规),但只要复制函数在自己的翻译单元中而不是被内联,就几乎没有什么可担心的。忘记我关于别名的写法;如果您以32位值交换数据,则几乎肯定实际上它最初就是32位值,而不是某种被强制转换的指针类型,因此没有问题。

1
编译器的字节交换内置函数是一种更好的方法,可以确保使用正确的指令。但这不是问题所在。 - user172783
@R.. gcc 仅优化长整型/指针大小的 bswap(i386 上为 32 位,x86-64 上为 64 位)。倒置其他大小的字节顺序仍会导致需要分别处理每个掩码,即使使用了 gcc 4.9 和 -O3。看起来它只针对单个显式用例进行了优化。同样的情况也适用于 clang(直到 3.3 版本)。 - Netch
@Netch:对于16位版本,GCC使用xchg %ah,%al。您声称它无法优化的情况是什么? - R.. GitHub STOP HELPING ICE
在32位系统上,它无法优化64位字节交换。而在64位系统上,令人惊讶的是,它无法优化32位字节交换:( - Netch
@R.. 我收到的最精确的描述:http://segfault.kiev.ua/~netch/articles/20131219-bswap.txt - Netch
显示剩余4条评论

3
在Linux中,您应该查看头文件bits/byteswap.h。有一个以 bswap_## 形式命名的宏族列出,其中一些在适当情况下使用汇编指令。

2
这个头文件写得太糟糕了。它们使用内联汇编使其“快速”,然后使用GCC扩展来优先选择C而不是汇编语言,这样当参数为常量时,GCC可以折叠它们。但是 - 如果不是那么悲哀,这就是让人感到有些好笑的地方 - 如果你只是像我在答案中写的那样编写朴素的C代码,GCC会自己生成相同或更好的汇编代码。 - R.. GitHub STOP HELPING ICE
@R. 它包含16、32、64位实现,并正确处理32/64位系统。并使其像bswap_16(...)一样简单。 - Foo Bah
我认为这些函数很有用。但是我认为头文件写得非常糟糕。如果将所有的内联汇编和gcc扩展都删除掉,生成的代码将会一样好甚至更好,并且几乎可以消除错误和不兼容性的可能性。此外,整理它将有助于教新手不要进行过早的优化... - R.. GitHub STOP HELPING ICE

1

是的,确实存在与问题中链接的类似函数,但由于数据的规模(在这种情况下)意味着设置开销太大,所以不值得努力。因此,最好只是每次读取2、4和8个字节,并使用指令集进行交换,然后写回。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接