Reducing memory usage in C# (for Online purposes)

I guess this is pretty obvious for most programmers, but anyway: As you can see from previous posts, I'm working with GameSparks, which is quite popular BAAS. They charge depending on memory usage, among other considerations so it's better to keep things small.

I need to send to the server the player input very often: (x,y) position, rotation, block type, and so on. Here you have the original input packet I was sending.

public class PlayerInputPacket
{
 //This could be improved using an int and bit shift ops: 1 byte for position.x another byte for position.y, 3 bits x block type, 2 bits x rotation, 4 bits x movement (we have 1 free byte)
 public Vector2   position; //Position in the grid 
 public BlockType blockType;
 public int  rotation; //Valid values: [0,3]
 public Movement  movement;

}

Since I'm working on a social game which has a grid with elements, I'm working with 2d grid coordinates that runs at most from 0 to 20 in case of the x coordinate, 0 to 40 in case of the y coordinate. I don't need 32 bit integers to express such small numbers, 5 bits for the x coordinate and 6 for the y coordinate it's enough.

Something similar applies to other input parameters, summarizing:

  • x position: 5 bits, as x coordinate runs from 0 to 20.
  • y position: 6 bits, [0.40]
  • Block Types: 3 bits as they are 8 different block types.
  • Rotation: needs 2 bits (its values runs from 0 to 3)
  • Movement:needs 3 bits, but I'll leave 4 bits for future features.


To put all those bits together I have to shift the original values. For example, I have to shift the x 27 bits to the left to leave the 5 bits I want to keep as is.
  uint result = 0;
  result = ((uint) _input.position.x) << 27;

To put the 'y' bits next to the 'x' bits I have to shift the y variable 21 bits to the left and make an OR with the previous value, that keeps the previous 5 bits untouched and leaves 6 bits next to the x bits to put the 'y' value.

  result = result | (((uint) _input.position.y)  << 21); 
Same logic applies to the block type, rotation and movement.

In the other side, when you receive the integer with all those bytes together, you have to mask it to isolate the bits you are interested in and shift to the right the same number of bits you shifted left before:

  //Note 0xF8000000 has the 5 most significant bits set to 1
  //0xF8000000 = 0b1111 1000 0000 0000 0000 0000 00000
  result.position.x = (float) ((_input & 0xF8000000) >> 27);
  
Here is the complete code:

public class PlayerInputPacket
{
 //This could be improved using an int and bit shift ops: 1 byte for position.x another byte for position.y, 3 bits x block type, 2 bits x rotation, 4 bits x movement (we have 1 free byte)
 public Vector2   position; //Position in the grid 
 public BlockType blockType;
 public int   rotation; //Valid values: [0,3]
 public Movement  movement;

 private static uint X_MASK    = 0xF8000000; //5 bits (x is in the range [0,20] at most, and 32 is the next power of two
 private static uint Y_MASK    = 0x07E00000; //6 bits
 private static uint BLOCK_TYPE_MASK = 0x001C0000; //3 most significant bits from the nibble
 private static uint ROTATION_MASK  = 0x00030000; //2 bits
 private static uint MOVEMENT_MASK = 0x0000F000; //4 bits
 private static uint FREE_SPACE_MASK = 0x00000FFF; //We have 12 bits free here

 private static int X_SHIFT    = 27; //32 - 5
 private static int Y_SHIFT    = 21; //32 - 5 - 6
 private static int BLOCK_TYPE_SHIFT = 18; //32 - 5 - 6 - 3
 private static int ROTATION_SHIFT  = 16; //32 - 5 - 6 - 3 - 2
 private static int MOVEMENT_SHIFT  = 12; //32 - 5 - 6 - 3 - 2 - 4

 public static uint Compress(PlayerInputPacket _input)
 {
  uint result = 0;

  result = result | (((uint) _input.position.x)  << X_SHIFT); 
  result = result | (((uint) _input.position.y)  << Y_SHIFT); 
  result = result | (((uint) _input.blockType)  << BLOCK_TYPE_SHIFT); 
  result = result | (((uint) _input.rotation)  << ROTATION_SHIFT); 
  result = result | (((uint) _input.movement)  << MOVEMENT_SHIFT); 

  return result;
 }

 public static PlayerInputPacket Uncompress(uint _input)
 {
  PlayerInputPacket result = new PlayerInputPacket ();

  result.position.x  = (float)  ((_input & X_MASK) >> X_SHIFT);
  result.position.y  = (float)  ((_input & Y_MASK) >> Y_SHIFT);
  result.blockType  = (BlockType) ((_input & BLOCK_TYPE_MASK) >> BLOCK_TYPE_SHIFT);
  result.rotation  = (int)   ((_input & ROTATION_MASK)  >> ROTATION_SHIFT);
  result.movement  = (Movement) ((_input & MOVEMENT_MASK)  >> MOVEMENT_SHIFT);

  return result;
 }
}

Comentarios

Entradas populares