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

标签 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       = 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%。 如何加快编码速度?

例行公事:(根据下面 Paul Smith 的建议进行了更新)

        public static long GetCombinedId(int largeId, int x, int y, int z)
        {

            var _largeId = (long)largeId;
            var _x = (long)x;
            var _y = (long)y;
            var _z = (long)z;

            return (_largeId << 48) | (_x << 32) | (_y << 16) | _z;

        }

        public static long GetCombinedId2(int largeId, int x, int y, int z)
        {
            return ((long)largeId << 48) | ((long)x << 32) | ((long)y << 16) | (long)z;
        }

        public static long GetCombinedId3(int largeId, int x, int y, int z)
        {
            unchecked
            {
                return ((long)(largeId << 16 | x) << 32) | (y << 16 | z );
            }

        }



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

            largeId = (int)(id >> 48);

            x = (int)((id >> 32) & 0x0000_0000_0000_FFFF);
            y = (int)((id >> 16) & 0x0000_0000_0000_FFFF);
            z = (int)(id & 0x0000_0000_0000_FFFF);


        }

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

            largeId = (int)(id >> 48);

            x = (int)((id << 16 ) >> 48);
            y = (int)((id << 32 ) >> 48);
            z = (int)((id << 48 ) >> 48);

        }
<小时/>

答案部分中描述的 Paul Smith 技术的变体

  [StructLayout(LayoutKind.Explicit)]
        public struct Mapper
        {
            [FieldOffset(0)] public Int64 Combined;
            [FieldOffset(0)] public Int16 Short0;
            [FieldOffset(2)] public Int16 Short1;
            [FieldOffset(4)] public Int16 Short2;
            [FieldOffset(6)] public Int16 Short3;
        }

        public static long GetId4(int largeId, int x, int y, int z)
        {

            Mapper mapper = new Mapper()
            {
                Short0 = (Int16)z,
                Short1 = (Int16)y,
                Short2 = (Int16)x,
                Short3 = (Int16)largeId
            };

            return mapper.Combined;

        }

        private static Mapper _mapper = new Mapper();
        public static long GetId5(int largeId, int x, int y, int z)
        {

            _mapper.Short0 = (Int16)z;
            _mapper.Short1 = (Int16)y;
            _mapper.Short2 = (Int16)x;
            _mapper.Short3 = (Int16)largeId;

            return _mapper.Combined;
        }

        [StructLayout(LayoutKind.Explicit)]
        public struct Mapper2
        {
            [FieldOffset(0)] public Int64 Combined;
            [FieldOffset(0)] public Int32 Integer0;
            [FieldOffset(4)] public Int32 Integer1;
        }

        private static Mapper2 _mapper2 = new Mapper2();
        public static long GetId6(int largeId, int x, int y, int z)
        {


            _mapper2.Integer0 = y << 16 | z;   //dangerous because we aren't checking upper bits of z
            _mapper2.Integer1 = largeId << 16 | x; //dangerous because we aren't checking upper bits of x


            return _mapper2.Combined;
        }
<小时/>

结果:

GetId1 = 2168ms
GetId2 = 1824ms
GetId3 = 1679ms
GetId4 = 2217ms
GetId5 = 2008ms
GetId6 = 1757ms
GetCoord1 = 785ms
GetCoord2 = 865ms
Routine1: 71776849217913036   binary: 11111111000000001010101000000000101110110000000011001100
Routine2: 71776849217913036   binary: 11111111000000001010101000000000101110110000000011001100
Routine3: 71776849217913036   binary: 11111111000000001010101000000000101110110000000011001100
Routine4: 71776849217913036   binary: 11111111000000001010101000000000101110110000000011001100
Routine5: 71776849217913036   binary: 11111111000000001010101000000000101110110000000011001100
Routine6: 71776849217913036   binary: 11111111000000001010101000000000101110110000000011001100
255, 170, 187, 204
255, 170, 187, 204

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

(仅供引用...BitConverter 类非常慢,并且由于不可行而被删除)

最佳答案

5 天后,我在洗澡时突然想到一个问题...如果将返回值从本地堆栈移动到调用过程堆栈中而耗尽了时间怎么办?事实证明……确实如此。

下面的新方法采用上面最快的方法(方法#3),而不是返回变量(这会导致“堆栈到堆栈”复制),而是传入返回值作为“输出”引用。这允许直接在调用过程的结果变量中执行计算。

这样做...我现在可以比解码更快编码,这就是我们一直以来的目标。以下是新例程和速度比较。

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

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

        }

速度比较。 GetId7 显示新结果:

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

并不是说需要它,但我很好奇是否有人可以更快地获得它。

关于c# - 从四个 16 位值构建 64 位 int 的最快方法?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/60144072/

相关文章:

java - 管理大量 boolean 状态的最有效方法

c# - 在 session 中使用键/值集合

c# - 如何防止 SQL 注入(inject)转义字符串

c# - 如何创建具有相同签名但不同泛型类型的扩展方法?

c# - 用标志方法扩展枚举?

c# - 通过 ITextSharp 从波斯语 html 文件创建 pdf

c# - 在 gridview 中单击编辑时在 Jquery 中显示/隐藏 Accordion 面板?

c# - 如何从 Windows 服务发送电子邮件?

c# - linq Except() 方法的意外行为

c - 左移-1位