Solution to slow BinaryReader on mobile devices

1 minute read

When working on Vorn’s Adventure in Xamarin.Android, I noticed a severe slowdown when using BinaryReader to read byte by byte a certain file with animated model. It came by surprise, hence why I share this in a post.

private AssetManager _assetManager;

private void Test(string filePath)
{
	using (var stream = _assetManager.Open(filePath))
	{
		PerformBinaryReading(stream);
	}
}

private void PerformBinaryReading(Stream fs)
{
	BinaryReader br = new BinaryReader(fs);
	//...
}

The binary file itself was relatively small (3MB) and on my PC it took around a single second to read it, while on a quite decent (spec-wise) Android phone it took a whooping 17 seconds.

First, let’s think why it happened. When we are using a BinaryReader and doing a lot of seeking back and forth in the bytes, the file is continuously accessed (locked and unlocked) from the storage. For a modern PC equipped with fast Ryzen CPU and NVMe SSD this is not a big deal, but for a mobile device this is real problem.

The trick to solve this problem is to read quickly the whole file into RAM in a single burst and then use the BinaryReader without any changes, so this does not involve to much work codewise. To do so, read the file into a byte array and then pass this byte array to the MemoryStream.

private AssetManager _assetManager;

public byte[] GetFileBytes(string filePath)
{
	var bytes = default(byte[]);
	using (StreamReader reader = new StreamReader(_assetManager.Open(filePath)))
	{
		using (var memstream = new MemoryStream())
		{
			reader.BaseStream.CopyTo(memstream);
			bytes = memstream.ToArray();
		}
	}
	return bytes;
}

private void Test(string filePath)
{
	byte[] fileContents = GetFileBytes(filePath);

	using (MemoryStream stream = new MemoryStream(fileContents))
	{
		PerformBinaryReading(stream);
	}
}

private void PerformBinaryReading(Stream fs)
{
	BinaryReader br = new BinaryReader(fs);
	//...
}

What happened is I read all the bytes to memory. Then, I created a MemoryStream with these bytes and BinaryReader instance to read bytes from the MemoryStream (essentially RAM). And as a result in my case, the performance on mobile device improved from 17 seconds to around 2 seconds.

Comments