In last week’s primer on the new domain memory (“Alchemy”) opcodes the initial test showed that they couldn’t match the performance of good old Vector when writing out a lot of float/Number values. Today’s article expands on that test to check the performance of writing integers and the performance of reading integers and float/Number values. Can the domain memory opcodes redeem themselves? Read on to find out.

As with last week’s primer, the goal of the “write” tests is to fill a Vector or contiguous block of the domain memory ByteArray with values. In today’s test that means writing int, uint, and Number values to a Vector and 8-, 16-, and 32-bit integers as well as 32- and 64-bit floating point values to the domain memory ByteArray. Here’s the source code:

package
{
	import flash.utils.Endian;
	import avm2.intrinsics.memory.si32;
	import avm2.intrinsics.memory.si16;
	import avm2.intrinsics.memory.si8;
	import avm2.intrinsics.memory.sf64;
	import avm2.intrinsics.memory.sf32;
	import avm2.intrinsics.memory.li32;
	import avm2.intrinsics.memory.li16;
	import avm2.intrinsics.memory.li8;
	import avm2.intrinsics.memory.lf64;
	import avm2.intrinsics.memory.lf32;
	import flash.system.ApplicationDomain;
	import flash.utils.getTimer;
	import flash.utils.ByteArray;
	import flash.text.TextFieldAutoSize;
	import flash.display.StageScaleMode;
	import flash.display.StageAlign;
	import flash.text.TextField;
	import flash.display.Sprite;
 
	public class FillInts extends Sprite
	{ 
		private var __logger:TextField = new TextField();
		private function row(...cols): void
		{
			__logger.appendText(cols.join(",")+"\n");
		}
 
		public function FillInts()
		{
			stage.align = StageAlign.TOP_LEFT;
			stage.scaleMode = StageScaleMode.NO_SCALE;
 
			__logger.autoSize = TextFieldAutoSize.LEFT;
			addChild(__logger);
 
			init();
		}
 
		private function init(): void
		{
			const SIZE:uint = 10000000;
 
			var i:uint;
			var vecInt:Vector.<int> = new Vector.<int>(SIZE);
			var vecUInt:Vector.<int> = new Vector.<int>(SIZE);
			var vecNumber:Vector.<Number> = new Vector.<Number>(SIZE);
			var domainMemory:ByteArray = new ByteArray();
			domainMemory.length = SIZE*19;
			domainMemory.endian = Endian.LITTLE_ENDIAN;
			var int32s:uint = 0;
			var int16s:uint = SIZE*4;
			var int8s:uint = int16s + SIZE*2;
			var float64s:uint = int8s + SIZE;
			var float32s:uint = float64s + SIZE*8;
			var curAddr:uint;
			var beforeTime:int;
			var afterTime:int;
			var valInt:int;
			var valUInt:uint;
			var valNumber:Number;
 
			ApplicationDomain.currentDomain.domainMemory = domainMemory;
 
			row("Storage", "Write Time");
 
			beforeTime = getTimer();
			for (i = 0; i < SIZE; ++i)
			{
				vecInt[i] = i;
			}
			afterTime = getTimer();
			row("Vector (int)", (afterTime-beforeTime));
 
			beforeTime = getTimer();
			for (i = 0; i < SIZE; ++i)
			{
				vecUInt[i] = i;
			}
			afterTime = getTimer();
			row("Vector (uint)", (afterTime-beforeTime));
 
			beforeTime = getTimer();
			for (i = 0; i < SIZE; ++i)
			{
				vecNumber[i] = i;
			}
			afterTime = getTimer();
			row("Vector (Number)", (afterTime-beforeTime));
 
			beforeTime = getTimer();
			curAddr = int32s;
			for (i = 0; i < SIZE; ++i)
			{
				si32(i, curAddr);
				curAddr += 4;
			}
			afterTime = getTimer();
			row("Domain Memory (int32)", (afterTime-beforeTime));
 
			beforeTime = getTimer();
			curAddr = int16s;
			for (i = 0; i < SIZE; ++i)
			{
				si16(i, curAddr);
				curAddr += 2;
			}
			afterTime = getTimer();
			row("Domain Memory (int16)", (afterTime-beforeTime));
 
			beforeTime = getTimer();
			curAddr = int8s;
			for (i = 0; i < SIZE; ++i)
			{
				si8(i, curAddr);
				curAddr++;
			}
			afterTime = getTimer();
			row("Domain Memory (int8)", (afterTime-beforeTime));
 
			beforeTime = getTimer();
			curAddr = float64s;
			for (i = 0; i < SIZE; ++i)
			{
				sf64(i, curAddr);
				curAddr++;
			}
			afterTime = getTimer();
			row("Domain Memory (float64)", (afterTime-beforeTime));
 
			beforeTime = getTimer();
			curAddr = float32s;
			for (i = 0; i < SIZE; ++i)
			{
				sf32(i, curAddr);
				curAddr++;
			}
			afterTime = getTimer();
			row("Domain Memory (float32)", (afterTime-beforeTime));
 
			row("Storage", "Read Time");
 
			beforeTime = getTimer();
			for (i = 0; i < SIZE; ++i)
			{
				valInt = vecInt[i];
			}
			afterTime = getTimer();
			row("Vector (int)", (afterTime-beforeTime));
 
			beforeTime = getTimer();
			for (i = 0; i < SIZE; ++i)
			{
				valUInt = vecUInt[i];
			}
			afterTime = getTimer();
			row("Vector (uint)", (afterTime-beforeTime));
 
			beforeTime = getTimer();
			for (i = 0; i < SIZE; ++i)
			{
				valNumber = vecNumber[i];
			}
			afterTime = getTimer();
			row("Vector (Number)", (afterTime-beforeTime));
 
			beforeTime = getTimer();
			curAddr = int32s;
			for (i = 0; i < SIZE; ++i)
			{
				valInt = li32(curAddr);
				curAddr += 4;
			}
			afterTime = getTimer();
			row("Domain Memory (int32)", (afterTime-beforeTime));
 
			beforeTime = getTimer();
			curAddr = int16s;
			for (i = 0; i < SIZE; ++i)
			{
				valInt = li16(curAddr);
				curAddr += 2;
			}
			afterTime = getTimer();
			row("Domain Memory (int16)", (afterTime-beforeTime));
 
			beforeTime = getTimer();
			curAddr = int8s;
			for (i = 0; i < SIZE; ++i)
			{
				valInt = li8(curAddr);
				curAddr++;
			}
			afterTime = getTimer();
			row("Domain Memory (int8)", (afterTime-beforeTime));
 
			beforeTime = getTimer();
			curAddr = float64s;
			for (i = 0; i < SIZE; ++i)
			{
				valNumber = lf64(curAddr);
				curAddr++;
			}
			afterTime = getTimer();
			row("Domain Memory (float64)", (afterTime-beforeTime));
 
			beforeTime = getTimer();
			curAddr = float32s;
			for (i = 0; i < SIZE; ++i)
			{
				valNumber = lf32(curAddr);
				curAddr++;
			}
			afterTime = getTimer();
			row("Domain Memory (float32)", (afterTime-beforeTime));
		}
	}
}

I ran this test in the following environment:

  • Release version of Flash Player 11.8.800.97
  • 2.3 Ghz Intel Core i7
  • Mac OS X 10.8.4
  • ASC 2.0 build 353448 (-debug=false -verbose-stacktraces=false -inline)

And here are the results I got:

Storage Write Time
Vector (int) 22
Vector (uint) 22
Vector (Number) 23
Domain Memory (int32) 24
Domain Memory (int16) 25
Domain Memory (int8) 23
Domain Memory (float64) 27
Domain Memory (float32) 28
Storage Read Time
Vector (int) 19
Vector (uint) 19
Vector (Number) 20
Domain Memory (int32) 16
Domain Memory (int16) 21
Domain Memory (int8) 17
Domain Memory (float64) 18
Domain Memory (float32) 19

Write Times

Read Times

In testing integers in addition to the floats last week we see that the performance is similarly worse than that of Vector regardless of the size of the integer being stored, even down to 8-bit. With reads the there is more of a mixed bag. Reading is mostly faster with domain memory with the exception of 16-bit integers. The margins here are small, these sorts of tests are notoriously fickle, and hardware will play a big role in the results. But overall it’s at least safe to say that domain memory is competitive with Vector at these tasks. More testing will be required to see if it can really shine elsewhere.

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