Если у меня есть три отдельных значения, которые могут вписаться в 32 бита, имеет ли смысл использовать uint для их хранения?
Я имею в виду, скажем, у меня есть struct
представлять некоторые данные, и это выглядит так:
struct LilStruct
{
public readonly short A;
public readonly byte B;
public readonly byte C;
public LilStruct(short a, byte b, byte c)
{
A = a;
B = b;
C = c;
}
}
short
и два byte
все значения могут вписаться в 32 бита. Что мне интересно, так это (для целей выравнивания, производительности, чего угодно), если бы на самом деле имело смысл хранить эти данные в следующем формате:
struct LilStruct
{
private readonly uint _value;
public LilStruct(short a, byte b, byte c)
{
_value = ((uint)a << 16) | ((uint)b << 8) | (uint)c;
}
public int A
{
get { return (int)(_value >> 16); }
}
public int B
{
get { return (int)(_value >> 8) & 0x000000FF; }
}
public int C
{
get { return (int)(_value & 0x000000FF); }
}
}
Это бессмысленно? Каковы будут преимущества / недостатки?
5 ответов
В.NET, когда вы собираетесь использовать struct
в любом случае, вы можете также украсить структуру StructLayoutAttribute
как это:
[StructLayout(LayoutKind.Sequential, Pack=1)]
struct LilStruct
{
public readonly short A;
public readonly byte B;
public readonly byte C;
public LilStruct(short a, byte b, byte c)
{
A = a;
B = b;
C = c;
}
}
Это приведет к тому, что поля будут расположены последовательно, например, поле B
начнется со смещения 16.
Значение 1 для Pack
означает, что поля выровнены по byte
границы.
Вы должны рассмотреть возможность объединения нескольких значений в uint, если значения тесно связаны друг с другом, обычно передаются вместе и никогда или очень редко изменяются независимо друг от друга. Стоимость распаковки и повторной упаковки uint с целью изменения его значения делает это очень дорогим (по размеру кода и времени выполнения) по сравнению с простым хранением трех байтов отдельно.
При выполнении на микроустройстве с общим объемом 10 Кбайт ОЗУ такая упаковка может стоить того, потому что память важнее скорости выполнения. На обычном настольном ПК или даже мобильном устройстве эта упаковка, вероятно, не стоит усилий.
Вы можете остаться со своим определением структуры и применить атрибут StructLayout со значением StructLayoutAttribute.Pack, равным 1. Но на самом деле вы, вероятно, сэкономите немного памяти за счет скорости доступа, поскольку таким образом данные не будут выложены в памяти наиболее эффективным способом. Компилятор обычно размещает память так, чтобы к ней было удобно обращаться, и она не будет автоматически портить слишком много памяти.
Этот подход, по крайней мере, сделает ваш код более понятным, чем предложенный вами метод сдвига битов (который на самом деле может или не может быть аналогичным тому, что компилятор машинного кода будет генерировать из байтового кода).
С чем вы столкнетесь - это простой компромисс между размером ваших данных в памяти и стоимостью обработки. Если память представляет собой реальную проблему, может быть просто дешевле использовать больше памяти на вашем компьютере.
Есть несколько случаев, когда стоит втиснуть материал в целое число. Пространство памяти обычно не является хорошей причиной, но если значения будут использоваться вместе, например, как ключ для словаря, словарь (целочисленный, безотносительно) будет намного быстрее, чем словарь (некоторой структуры, безотносительно).