A Boolean in AS3 takes up four bytes of memory to store a single bit of information. It takes up 32x more memory than it needs. We can make better use of this memory and today’s article explains how.

An int, a uint, and a Boolean all take up 32 bits (4 bytes) of memory. A Boolean allows two values: true and false. An int allows 2^32 values: -2,147,483,648 to 2,147,483,647. A uint allows 2^32 values: 0 to 4,294,967,295. We could just as easily store 0 for false and anything else (e.g. 1, 42, -100) as true in an int or a uint.

Some would argue that true and false are more readable than 0 and non-zero and that the implication of a Boolean type is more specific (“put true or false here”) rather than the implications of an int or uint type (“put any integral value here”). Good variable, field, and parameter naming can help overcome some of these concerns, but AS3 is not as well equipped to really level the playing field. Readability will likely suffer from the switch from Boolean to integers, but other areas will improve.

If you do decide to make the switch, you’ll gain a lot of flexibility from all those newly-accessible bits. One of the best things you can do is designate each bit as its own mini-Boolean. For example, consider using a uint as a “bit field” for a file:

Bit Index Meaning of 1
0 Is a directory
1 Is a symbolic link (symlink)
2 Read only

You could certainly add more and more bits to this. Now consider a function that gets file information:

static const MASK_IS_DIRECTORY:uint = 1;
static const MASK_IS_SYMLINK:uint = 2;
static const MASK_IS_READONLY:uint = 4;
 
function getFileInfo(file): uint
{
    var ret:uint;
    if (file is a directory)
    {
        ret |= MASK_IS_DIRECTORY;
    }
    if (file is a symlink)
    {
        ret |= MASK_IS_SYMLINK;
    }
    if (file is read only)
    {
        ret |= MASK_IS_READONLY;
    }
    return ret;
}
 
var fileInfo:uint = getFileInfo(file);
if (fileInfo & MASK_IS_DIRECTORY)
{
    trace(file + " is a directory");
}
if (fileInfo & MASK_IS_SYMLINK)
{
    trace(file + " is a symbolic link");
}
if (fileInfo & MASK_IS_READONLY)
{
    trace(file + " is read-only");
}

Here we’ve created three “mask” variables that are used to get and set each bit in the bit field. By using the bitwise “or” (|) operator, we can set these bits: field |= MASK. Here we’ve created three “mask” variables that are used to get and set each bit in the bit field. Unsetting a bit is similar: field &= ~MASK. By using the bitwise “and” (&) operator, we can get these bits: field &= MASK. A simple uint is all we need to store up to 32 boolean values. Here’s a cheat sheet:

// Set to true
field |= MASK
 
// Set to false (unset)
field &= ~MASK
 
// Get
if (field & MASK)

Now consider the alternative for this function: a class with a bunch of Boolean values in it.

class FileInfo
{
    var isDirectory:Boolean;
    var isSymlink:Boolean;
    var isReadOnly:Boolean;
}
 
function getFileInfo(file): FileInfo
{
    var ret:FileInfo = new FileInfo();
    if (file is a directory)
    {
        ret.isDirectory = true;
    }
    if (file is a symlink)
    {
        ret.isSymlink = true;
    }
    if (file is read only)
    {
        ret.isReadOnly = true;
    }
    return ret;
}
 
var fileInfo:FileInfo = getFileInfo(file);
if (fileInfo.isDirectory)
{
    trace(file + " is a directory");
}
if (fileInfo.isSymlink)
{
    trace(file + " is a symbolic link");
}
if (fileInfo.isReadOnly)
{
    trace(file + " is read-only");
}

We know that a class with three Boolean values takes up 48 bytes of memory. The uint takes up only 4. That’s 12x less memory! There’s also the garbage collector to keep in mind. The getFileInfo function now allocates a FileInfo instance each time it’s called. This must be allocated by the VM and tracked by the GC. The instance is probably immediately thrown away, which triggers the GC to collect the memory. Call this function a lot and you could have a serious performance problem on your hands.

To get around this issue, you need to reuse FileInfo instances by filling in the fields rather than creating a new one each time:

function getFileInfo(file, info:FileInfo): void
{
    if (file is a directory)
    {
        info.isDirectory = true;
    }
    else
    {
        info.isDirectory = false;
    }
    if (file is a symlink)
    {
        info.isSymlink = true;
    }
    else
    {
        info.isSymlink = false;
    }
    if (file is read only)
    {
        info.isReadOnly = true;
    }
    else
    {
        info.isReadOnly = false;
    }
}
 
static const REUSED_FILE_INFO:FileInfo = new FileInfo();
 
getFileInfo(file, REUSED_FILE_INFO);
if (REUSED_FILE_INFO.isDirectory)
{
    trace(file + " is a directory");
}
if (REUSED_FILE_INFO.isSymlink)
{
    trace(file + " is a symbolic link");
}
if (REUSED_FILE_INFO.isReadOnly)
{
    trace(file + " is read-only");
}

The code is now arguably less readable than the version that simply used a uint as a bit field.

Lastly, let’s make sure that using a uint as a bit field doesn’t have any adverse performance effects compared to using a Boolean. The following test program checks just that:

package
{
	import flash.display.*;
	import flash.utils.*;
	import flash.text.*;
 
	public class DataDensity extends Sprite
	{
		private var logger:TextField = new TextField();
		private function row(...cols): void
		{
			logger.appendText(cols.join(",") + "\n");
		}
 
		private static const MASK_SOME_FLAG:uint = 0x4;
 
		public function DataDensity()
		{
			stage.align = StageAlign.TOP_LEFT;
			stage.scaleMode = StageScaleMode.NO_SCALE;
 
			logger.autoSize = TextFieldAutoSize.LEFT;
			addChild(logger);
 
			init();
		}
 
		private function init(): void
		{
			const REPS:uint = 100000000;
			var i:uint;
			var val:uint;
			var valTrue:uint = 0x4;
			var valFalse:uint = 0x0;
			var sum:int;
			var bool:Boolean;
			var boolTrue:Boolean = true;
			var boolFalse:Boolean = false;
			var beforeTime:int;
			var afterTime:int;
 
			row("Operation", "Time");
 
			beforeTime = getTimer();
			for (i = 0; i < REPS; ++i)
			{
				if (boolTrue)
				{
					sum++;
				}
			}
			afterTime = getTimer();
			row("Boolean (true)", (afterTime-beforeTime));
 
			beforeTime = getTimer();
			for (i = 0; i < REPS; ++i)
			{
				if (boolFalse)
				{
					sum++;
				}
			}
			afterTime = getTimer();
			row("Boolean (false)", (afterTime-beforeTime));
 
			beforeTime = getTimer();
			for (i = 0; i < REPS; ++i)
			{
				if (valTrue & MASK_SOME_FLAG)
				{
					sum++;
				}
			}
			afterTime = getTimer();
			row("Flags (true)", (afterTime-beforeTime));
 
			beforeTime = getTimer();
			for (i = 0; i < REPS; ++i)
			{
				if (valFalse & MASK_SOME_FLAG)
				{
					sum++;
				}
			}
			afterTime = getTimer();
			row("Flags (false)", (afterTime-beforeTime));
		}
	}
}

I tested this app using the following environment:

  • Release version of Flash Player 13.0.0.214
  • 2.3 Ghz Intel Core i7-3615QM
  • Mac OS X 10.9.2
  • Google Chrome 35.0.1916.114
  • ASC 2.0.0 build 354130 (-debug=false -verbose-stacktraces=false -inline -optimize=true)

And got these results:

Operation Time
Boolean (true) 235
Boolean (false) 189
Flags (true) 219
Flags (false) 187

Data Density Performance Graph

If anything, the bit fields seem slightly faster than the Boolean values. These results are over 100 million iterations though, so the advantage is very slight.

In conclusion, consider using bit fields over Boolean values to save memory and GC time. Readability is always arguable, but the data density improvements are clear. You’ll store 32x more than with Boolean

Spot a bug? Have a question or suggestion? Post a comment!