我在 C# 中有一个 uint-types 数组,在检查程序是否在 little-endian 机器上运行后,我想将数据转换为 big-endian 类型。因为数据量可能会变得非常大但总是均匀的,所以我在考虑将两种 uint 类型视为 ulong 类型,以获得更好的性能并在 ASM 中对其进行编程,所以我正在寻找一个非常快(如果可能的话最快) 汇编器算法将小端转换为大端。
3 回答
对于大量数据,bswap
指令(在 Visual C++ 中的_byteswap_ushort
、_byteswap_ulong
和_byteswap_uint64
内在函数下可用)是要走的路。这甚至会胜过手写组装。这些在没有 P/Invoke 的纯 C# 中不可用,因此:
- 仅当您有大量数据要进行字节交换时才使用此选项。
- 您应该认真考虑在托管 C++ 中编写最低级别的应用程序 I/O,这样您就可以在将数据放入托管数组之前进行交换。您已经必须编写一个 C++ 库,因此不会有太多损失,并且您可以回避所有与 P/Invoke 相关的性能问题,以便在大型数据集上运行低复杂度算法。
PS:很多人不知道字节交换内在函数。它们的性能令人惊讶,对于浮点数据更是如此,因为它将它们作为整数处理。如果不为每个单字节交换用例手动编码寄存器负载,就无法击败它,如果您尝试这样做,您可能会在优化器中受到比您所接受的更大的打击。
您可能想简单地重新考虑问题,这不应该是一个瓶颈。采用朴素算法(用 CLI 汇编编写,只是为了好玩)。假设我们想要的数字是本地数字 0
LDLOC 0
SHL 24
LDLOC 0
LDC.i4 0x0000ff00
SHL 8
OR
LDLOC 0
LDC.i4 0x00ff0000
SHL.UN 8
OR
LDLOC 0
SHL.UN 24
OR
每个数字最多有 13 条 (x86) 汇编指令(而且解释器很可能会通过使用聪明的寄存器变得更加智能)。没有比这更天真的了。
现在,将其与成本进行比较
- 加载数据(包括您正在使用的任何外围设备!)
- 数据处理(例如进行比较)
- 输出结果(无论是什么)
如果每个数字 13 条指令占您执行时间的很大一部分,那么您正在执行一项非常高性能的任务,并且应该以正确的格式输入!您也可能不会使用托管语言,因为您希望对数据缓冲区和其他内容进行更多控制,并且不需要额外的数组边界检查。
如果该数据数组来自网络,我希望管理套接字的成本比单纯的字节顺序翻转要高得多,如果它来自磁盘,请考虑在执行此程序之前进行预翻转。
我正在考虑将两种 uint 类型视为 ulong 类型
好吧,这也会交换两个 uint 值,这可能是不可取的......
您可以在不安全模式下尝试一些 C# 代码,这实际上可能表现得足够好。像:
public static unsafe void SwapInts(uint[] data) {
int cnt = data.Length;
fixed (uint* d = data) {
byte* p = (byte*)d;
while (cnt-- > 0) {
byte a = *p;
p++;
byte b = *p;
*p = *(p + 1);
p++;
*p = b;
p++;
*(p - 3) = *p;
*p = a;
p++;
}
}
}
在我的计算机上,吞吐量约为每秒 2 GB。