Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Byte vs Byte Array in Memory

Tags:

c#

byte

The code below uses 1 byte of memory.

byte[] n = new byte[] { 10, 20, 30, 40 }; //memory looks like this 10 | 20 | 30 | 40

vs this code below which uses 4 bytes of memory.

byte n1 = 10; // memory looks like this | 10 | 0 | 0 | 0 | 20 | 0 | 0 | 0 | 30 | 0 | 0.....
byte n2 = 20;
byte n3 = 30;
byte n4 = 40;

This is tested in Visual Studio 2012 + 2010, I thought modern compilers were supposed to do optimization for me? If it truly is faster to put spacing so that the byte secretly becomes a non-functional int than how come it doesn't do the same for arrays and would a byte array thus become faster if it occupied 4 bytes per value thus rendering the whole purpose of byte completely useless on a 32 bit / 64 bit machine? To summarize: Why does the compiler allocate the memory these two different ways and which one is the inefficient way? Part 2 what is the actual purpose of a byte on a 32 bit / 64 bit machine if it is so inefficient to store it in an actual contiguous byte block as VS refuses to do when you declare bytes individually.

like image 388
CodeCamper Avatar asked Dec 20 '22 01:12

CodeCamper


2 Answers

I assume there are really two questions here:

Why does the compiler not pack four bytes into a single Int32

Local variables are not typically optimized for storage, but for speed of access. As the speed of access to a single unaligned byte is sometimes not possible in a single instruction and, until recently (2009), is an order of magnitude slower than aligned address, compiler authors typically use aligned widths as a reasonable tradeoff.

Beyond that, the .Net Framework is spec'd not for x86 but for the Common Language Infrastructure virtual machine. The CLI spec must support the lowest common denominator and IA64 and ARM do not support non QWORD aligned memory access. To that end, the CLI stack "can only store values that are a minimum of 4 bytes wide" (P.330).

Why did they do this? I would imagine the potential or real performance gains outweigh the increase in memory usage. Given the additional limitation of 64 functional locals in any given scope, there should be a strong desire (beyond good design) to keep the number of variables in a given scope small. The net overhead is therefore limited to 192 bytes, which equates to an additional 0.0000002% of memory used in my system.

Keep in mind, if you are accessing an array of bytes, you are really storing a single pointer - which is the width of a memory address (4 or 8 bytes) and accessing memory directly. You are managing the semantics of which byte is which and take on that complexity.

How can I store things in a compact form to minimize memory usage

As you point out, if your data is a large number of bytes, use a byte array to avoid overhead. If your data is of varying types, use one of the many classes which allow for access of packed data (BinaryReader, BinaryWriter, BitConverter, unsafe code, structs with the StructLayout.Pack field set all come to mind).

If you have a ridiculous amount of data, use Memory Mapped Files with fixed layout structs to minimize memory use while still allowing for datasets larger than the amount of memory in the machine. Is it harder than normal memory access? Yes, yes it is - but optimization is a balancing act of managing memory usage, speed, and programmer labor. The cheapest one is typically memory.

Or, spend a couple hundred bucks and get enough ram that it doesn't matter. 32 GB ($240 USD on newegg) allows for quite a bit of not-caring for most situations.

like image 157
Mitch Avatar answered Dec 29 '22 12:12

Mitch


When you create an array of n bytes or n byte variables the compiler has different degrees of freedom to optimize. The array has a fixed memory layout since an array is a dense data structure with no padding between the values.

If you declare byte values on the other hand the JITer tries to align them on 4 or 8 byte boundaries (x64) to ensure aligned memory access. The effects of aligned vs non-aligned memory access can be around 30% for read access. This is a worthwile optimization. The holes between the bytes in this case is a non issue since less space will not make you faster. Correct memory alignment is the key to best performance.

like image 25
Alois Kraus Avatar answered Dec 29 '22 11:12

Alois Kraus