Click here to Skip to main content
15,860,844 members
Articles / Programming Languages / C#
Article

The ELMO Principle - Part 1 - Stack and Heap Usage

Rate me:
Please Sign up or sign in to vote.
3.09/5 (4 votes)
20 Dec 2007CPOL5 min read 29K   20   12
Utilizing the Etremely Low Memory Optimization principle.

Introduction

In today's modern computing world, there is an ever shrinking emphasis on efficient memory utilization. Because of the fact that it's now possible to store huge amounts of memory on tiny flash cards, most modern programming languages have long since abandoned micromanagement of memory in favor of class wrappers and pre-configured object models that make application development much more rapid. A great deal of the programmers out there have never experienced a time when memory and processor power were at a premium. Anyone familiar with the C programming language understands the complexities of memory management and the need for processor utilization.

But development practices often come full circle. As devices and systems get smaller and smaller, consumers demand more from less. A common cellular phone these days includes a wireless transmitter and receiver, a camera, an LCD screen, some amount of solid state memory, an operating system, and a full complement of applications. More complex phones include Bluetooth™ connectivity, touch screen displays, full internet connectivity, and an even wider suite of applications. These days, people want a desktop computer in the palm of their hand. And, they are able to get it, thanks to efficient code.

Background

With this in mind, I present Part 1 of my series relating to the "ELMO" principle. ELMO is an acronym that I came up with that stands for Extremely Low Memory Optimization, and aims to be a principle that everyday programmers can employ effectively and efficiently in all applications in order to trim the proverbial fat from their applications while not sacrificing readability or reuse. If you're interested, please read on.

Understanding Memory Usage

For starters, I think it's important to outline just how much memory each common data type will take up when created. Many of today's programmers don't understand the relationship between, say, Booleans and bytes, simply because Microsoft® has such a brilliantly designed IDE that much of the old guesswork has been taken out of the equation. That doesn't make it any less important, though. So, here's a quick overview regarding types pertaining to the .NET Framework 2.0.

  • Boolean – 1 byte (8 bits)
  • UShort/Short/Int16/Char – 2 bytes (16 bits)
  • Int32/int/float – 4 bytes (32 bits)
  • Long/ULong/Int64/double – 8 bytes (64 bits)
  • Decimal – 16 bytes (128 bits)

It's pretty easy to test this out in the .NET environment using unmanaged code, like so:

C#
static unsafe void Main(string[] args)
{
  Console.WriteLine("System.Boolean is: " +
    sizeof(Boolean) + " byte(s).");
  Console.WriteLine("System.Int16 is: " +
    sizeof(Int16) + " byte(s).");
  Console.WriteLine("System.Int32 is: " +
    sizeof(Int32) + " byte(s).");
  Console.WriteLine("System.Int64 is: " +
    sizeof(Int64) + " byte(s).");
  Console.WriteLine("Char is: " +
    sizeof(char) + " byte(s).");
  Console.WriteLine("Double is: " +
    sizeof(double) + " byte(s).");
  Console.WriteLine("Decimal is: " +
    sizeof(decimal) + " byte(s).");
  Console.ReadLine();
}

The above code outputs the following:

The size of a System.Boolean is: 1 byte(s).
The size of a System.Int16 is: 2 byte(s).
The size of a System.Int32 is: 4 byte(s).
The size of a System.Int64 is: 8 byte(s).
The size of a char is: 2 byte(s).
The size of a double is: 8 byte(s).
The size of a decimal is: 16 byte(s).

Applying ELMO

So, what does this mean to us? Well, it can dictate quite a bit about how we use types within our code. For the first case scenario, I'm going to pretend that we have the need to create an object capable of holding several Boolean values for us. For illustration, I'm going to say we need something that can hold 8 true or false (Boolean) values. Most application developers would start off with something that looks like the following implementation:

C#
public class CBoolHolder
{
    private bool _bool1, _bool2, _bool3, _bool4,
      _bool5, _bool6, _bool7, _bool8;
    public CBoolHolder(bool bool1, bool bool2, bool bool3,
      bool bool4, bool bool5, bool bool6, bool bool7, bool bool8)
    {
      _bool1 = bool1;
      _bool2 = bool2;
      _bool3 = bool3;
      _bool4 = bool4;
      _bool5 = bool5;
      _bool6 = bool6;
      _bool7 = bool7;
      _bool8 = bool8;
    }

    public bool Bool1
    {
      get { return _bool1; }
      set { _bool1 = value; }
    }

    // [ ..Additional property implementation ]
}

While there's nothing technically wrong with this approach, it uses way more code and memory than is needed! First, let's start off with something that can be slightly scary to some developers, and archaic to others: the structure! Yes, I said structure. It consumes very little memory, and can do all of the things we need it to do. Our first implementation might look like this:

C#
public struct BoolHolder
{
  public bool bool1, bool2, bool3, bool4, bool5,
    bool6, bool7, bool8;

  public BoolHolder(bool Bool1, bool Bool2,
    bool Bool3, bool Bool4, bool Bool5, bool Bool6,
    bool Bool7, bool Bool8)
  {
    bool1 = Bool1;
    bool2 = Bool2;
    bool3 = Bool3;
    bool4 = Bool4;
    bool5 = Bool5;
    bool6 = Bool6;
    bool7 = Bool7;
    bool8 = Bool8;
  }
}

Now, we have all of the same functionality, but it lives inside a struct on the stack instead of in an object on the heap. Better already! If we run our sizeof console application again, we get the following information:

The size of BoolHolder is: 8 byte(s).

OK, we're down to only 8 bytes, or 64 bits. Trimming down the fat already! So, what else can we do to squish down the memory usage and save space? Well, instead of using boolean values within the structure, how about we just use one single Int32? How, you might ask? Well, we're going to use bitwise operations. I'm not going to get into defining what they are here (for more information, visit the link I just provided), and I'm going to assume you know how bitwise operations work. If not, go and read up, then come back here to see how we can implement them into our structure like so:

C#
public struct BitHolder
{
  int bits;

  public BitHolder(int holder)
  {
    bits = holder;
  }

  public bool this[int index]
  {
    get
    {
      // Check the bit at index and return true or false
      return (bits & (1 << index)) != 0;
    }
    set
    {
      if (value)
      {
        // Sets the bit at given index to 1 (true)
        bits |= (1 << index);
      }
      else
      {
        // Sets the bit at given index to 0 (false)
        bits &= ~(1 << index);
      }
    }
  }

Using ELMO Objects

What this has done for us is the following:

  • We are using a single Int32 to hold 8 boolean values like so: 01001001.
  • We are utilizing an accessor (this[int index]) to retrieve binary values from the int, so this[0] would be 0, or false.
  • We set the return type of the accessor to boolean, and use bitwise operations to retrieve the given values.

In order to utilize our shiny new ELMO structure, we just do this:

C#
BitHolder holder = new BitHolder(0);
holder[0] = false;
holder[1] = true;
holder[2] = false;
holder[3] = false;
holder[4] = true;
holder[5] = false;
holder[6] = false;
holder[7] = true;

Wrap-Up

Now, a single structure holds 8 boolean values (the int would look like 01001001), but we can easily use this to pull up to 8 true or false bits from a single data type. So, how does it hold up to our memory test? Let's take a look:

The size of BitHolder is: 4 byte(s).

We halved it! We're only using 32 measly bits because we only needed to use one single puny Int32 for storage of all of our values. Not bad for the amount of data we can actually store. We could, if we so desired, halve the size again, but we'd only be able to store 4 boolean values instead of 8, using a short or Int16 type. What's more is that it's not so hard to get and set those values. We can do it iteratively, like so:

C#
for (int x=0; x<8; x++)
      Console.WriteLine(holder[x].ToString());

Or, value by value like we set them above (holder[x] = false). So, now you have an ultra-light, low memory structure instead of a fat and bloated class, that uses even less code. And, of course, structures can be nullable, and can be passed as arguments to constructors and methods for use and re-use.

Special Thanks

I'd like to thank Chandra Hundigam, Charles Wright, and Kris Jamsa for their articles that helped me illustrate my ideas behind ELMO.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior) CentralReach
United States United States
I have worked professionally in IT since 2004, and as a software architect since 2008, specializing in user interface design and experience, something I am still extremely passionate about. In 2013 I moved into management, and since then I've held positions as Director of Product Development, Director of Engineering, and Practice Director.

Comments and Discussions

 
GeneralStructs in the stack, classes in the heap?! Pin
AndresMlinar24-Dec-07 1:19
AndresMlinar24-Dec-07 1:19 
QuestionRe: Structs in the stack, classes in the heap?! Pin
chrisw27-Dec-07 6:36
chrisw27-Dec-07 6:36 
GeneralRe: Structs in the stack, classes in the heap?! Pin
DreamInHex27-Dec-07 11:01
DreamInHex27-Dec-07 11:01 
GeneralRe: Structs in the stack, classes in the heap?! Pin
AndresMlinar28-Dec-07 2:33
AndresMlinar28-Dec-07 2:33 
GeneralRe: Structs in the stack, classes in the heap?! Pin
chrisw28-Dec-07 3:01
chrisw28-Dec-07 3:01 
GeneralRe: Structs in the stack, classes in the heap?! Pin
AndresMlinar28-Dec-07 3:17
AndresMlinar28-Dec-07 3:17 
GeneralBitVector32 Pin
SimmoTech20-Dec-07 19:08
SimmoTech20-Dec-07 19:08 
GeneralRe: BitVector32 Pin
DreamInHex21-Dec-07 15:55
DreamInHex21-Dec-07 15:55 
GeneralRe: BitVector32 Pin
SimmoTech22-Dec-07 7:37
SimmoTech22-Dec-07 7:37 
GeneralRe: BitVector32 Pin
Expediteur26-Dec-07 0:34
Expediteur26-Dec-07 0:34 
GeneralRe: BitVector32 Pin
chrisw26-Dec-07 11:09
chrisw26-Dec-07 11:09 
GeneralRe: BitVector32 Pin
DreamInHex27-Dec-07 10:58
DreamInHex27-Dec-07 10:58 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.