C# 从四个16位值构建64位int的最快方法?

C# 从四个16位值构建64位int的最快方法?,c#,.net,bit-manipulation,64-bit,C#,.net,Bit Manipulation,64 Bit,基本上,我正在尝试从坐标中构建一个唯一的64位id,稍后我可以将其分离。这些操作将在短时间内执行数十亿次,因此速度至关重要。这就是我想要的。我有4-32位整数,但只有底部的16位是相关的。我想将底部的16位串接成一个64位的“长”(不管它是否有符号,因为这些位是相同的)。因此,如果我有: largeId = 0000 0000 0000 0000 0000 0000 1000 1000 x = 0000 0000 0000 0000 0000 0000 1100 1100 y

基本上,我正在尝试从坐标中构建一个唯一的64位id,稍后我可以将其分离。这些操作将在短时间内执行数十亿次,因此速度至关重要。这就是我想要的。我有4-32位整数,但只有底部的16位是相关的。我想将底部的16位串接成一个64位的“长”(不管它是否有符号,因为这些位是相同的)。因此,如果我有:

largeId = 0000 0000 0000 0000   0000 0000 1000 1000
x       = 0000 0000 0000 0000   0000 0000 1100 1100
y       = 0000 0000 0000 0000   0000 0000 1110 1110
z       = 0000 0000 0000 0000   0000 0000 1111 1111
它将成为:

Id = 0000 0000 1000 1000   0000 0000 1100 1100   0000 0000 1110 1110   0000 0000 1111 1111
我已经编写了一些产生期望结果(即构建和分离)的例程,并使用500^3次迭代对它们进行计时,以尝试找到最快的例程。将64位数字解码回4个int变量的例程运行时间约为编码所需时间的43%如何加快编码速度???

例行程序:(更新了以下保罗·史密斯的建议)

是否有更好/更快的方法将4个整数编码为64位长度?


(仅供参考…BitConverter类速度非常慢,由于不可行而被删除)

尚未对此进行计时,但这似乎可以避免移位和掩蔽

[StructLayout(LayoutKind.Explicit)]
public struct Mapper
{
   [FieldOffset(0)] public UInt64 Combined;
   [FieldOffset(1)] public UInt16 Short0;
   [FieldOffset(2)] public UInt16 Short1;
   [FieldOffset(3)] public UInt16 Short2;
   [FieldOffset(4)] public UInt16 Short3;
}
新建一个映射器,然后指定各种Shortx值。读回组合值

var test = new Mapper();
test.Short0 = 1;
test.Short1 = 16;
test.Short2 = 256;
test.Short3 = 4096;

然后,
test。合并后的
将是64位串联。

5天后,洗澡时我突然想到一个问题……如果将返回值从本地堆栈移到调用过程堆栈中,时间被消耗殆尽了怎么办?结果是…是的

下面的新方法采用上面最快的方法(方法#3),而不是返回变量(导致“stack to stack”复制),而是将返回值作为“out”引用传入。这使得计算可以直接从调用过程执行到结果变量中

这样做…我现在可以比解码更快地编码,这一直是我的目标。下面是新的例行程序和速度比较

        public static void GetId7(int largeId, int x, int y, int z, out long id)
        {

            id = ((long)(largeId << 16 | x) << 32) | (y << 16 | z);

        }

这并不是必须的,但我很好奇是否有人能更快地得到它。

在C/C++中,我会撒谎并将
int64 Id64
的地址转换为
short x[4]
,这样我就可以复制而不是移动。或者只使用memcpy。这可能比转换快,也可能不快。如果您使用的是.net core 3.0,请查看内部函数是否有帮助。是否有某种类型可以同时存储
largeId
x
y
z
,或者这些类型在传递到
getCombineId()
之前来自何处?您是否为
任何CPU
x64
编译?@BACON否,这些值是单独输入的。仅存储组合ID。现在我有它的任何设置,但我可能能够限制到x64。“我得做些研究。”戴维下面保罗·史密斯的回答与这项技术是一致的。我试过了,在几个版本之后,它几乎和我的第三个程序差不多。但不完全是这样。我用他的建议更新了我的问题。谢谢你的建议。Short0必须从0开始。还要记住,它们的大小是两个字节,所以偏移量应该增加2。我对这个解决方案很有希望,因为它看起来很光滑。然而,它的表现并不是很好。结构的分配破坏了您的精确方法。然后我在课堂上把这个结构变成了一个静态结构,它变得更快了。然后我将位移位与这种方法结合起来,速度更快。但我上面的方法3仍然略优于使用场偏移的最佳版本。不过,我正在用你们的解决方案和结果更新我的答案。所以谢谢你给了我其他的东西来检查@多米谢谢你的评论。在尝试此操作时为我省去了很多麻烦。
FieldOffset
值不需要分别为0,0,2,4,6,因为每个UInt16值都是2字节吗?当我使用这段代码时,我得到4097,16,04096的值作为结构成员的值。此外,如其他注释所述,当前偏移量
组合
Shortx
字段将不会占用相同的64位。
[StructLayout(LayoutKind.Explicit)]
public struct Mapper
{
   [FieldOffset(0)] public UInt64 Combined;
   [FieldOffset(1)] public UInt16 Short0;
   [FieldOffset(2)] public UInt16 Short1;
   [FieldOffset(3)] public UInt16 Short2;
   [FieldOffset(4)] public UInt16 Short3;
}
var test = new Mapper();
test.Short0 = 1;
test.Short1 = 16;
test.Short2 = 256;
test.Short3 = 4096;
        public static void GetId7(int largeId, int x, int y, int z, out long id)
        {

            id = ((long)(largeId << 16 | x) << 32) | (y << 16 | z);

        }
GetId1 = 2282ms
GetId2 = 1910ms
GetId3 = 1782ms
GetId4 = 2306ms
GetId5 = 2092ms
GetId6 = 1816ms
GetId7 = 831ms
GetCoord1 = 828ms
GetCoord2 = 930ms
Routine1: 71776849217913036   binary: 11111111000000001010101000000000101110110000000011001100
Routine2: 71776849217913036   binary: 11111111000000001010101000000000101110110000000011001100
Routine3: 71776849217913036   binary: 11111111000000001010101000000000101110110000000011001100
Routine4: 71776849217913036   binary: 11111111000000001010101000000000101110110000000011001100
Routine5: 71776849217913036   binary: 11111111000000001010101000000000101110110000000011001100
Routine6: 71776849217913036   binary: 11111111000000001010101000000000101110110000000011001100
Routine7: 71776849217913036   binary: 11111111000000001010101000000000101110110000000011001100
255, 170, 187, 204
255, 170, 187, 204