По сути, я пытаюсь создать уникальный 64-битный идентификатор из координат, которые потом можно будет потом разделить. Эти операции будут выполняться в миллиарды раз в короткие сроки, поэтому скорость имеет решающее значение. Вот то, что я после. У меня есть 4 - 32-битные целые числа, но релевантны только младшие 16 бит. Я хочу объединить нижние 16 бит в один 64-битный «long» (не имеет значения, подписан он или нет, поскольку биты одинаковые). Поэтому, если у меня есть:
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% времени, необходимого для их кодирования. Как я могу ускорить кодирование ??
Подпрограммы: (обновлено с изменениями из предложений Пола Смита ниже)
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);
}
Вариации техники Пола Смита, описанные в разделе ответов
[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 является парализующе медленным и был удален, потому что он не возможно)