Differences

This shows you the differences between two versions of the page.

random:net_sizeof_reference_type [2011/07/12 16:51] (current)
grant created
Line 1: Line 1:
 +====== .NET Sizeof Reference Type ======
 +
 +<code>
 +//
 +// Written by Grant Jenks
 +// http://www.grantjenks.com/
 +//
 +// DISCLAIMER
 +// THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE
 +// LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
 +// OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND,
 +// EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE
 +// ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.
 +// SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY
 +// SERVICING, REPAIR OR CORRECTION.
 +//
 +// Copyright
 +// This work is licensed under the
 +// Creative Commons Attribution-NonCommercial-NoDerivs 3.0 Unported License.
 +// To view a copy of this license, visit
 +// http://creativecommons.org/licenses/by-nc-nd/3.0/
 +// or send a letter to Creative Commons, 171 Second Street, Suite 300,
 +// San Francisco, California, 94105, US.A
 +//
 +// Description
 +// Recently I wondered about the size of a LinkedListNode<T> object. As I
 +// thought about it, I also wondered how large an Object was in .NET. I
 +// wrote this program as a means of estimating the size of a reference type.
 +// After reading around a bit online, it was clear that C# doesn't make
 +// this easy. The language's built-in "sizeof" operator works only on value
 +// types. In the forums and blogs, people suggested adding the size of all
 +// the fields using reflection or peeking at the object in a debugger /
 +// profiler. The first idea that came to me was neither of these options.
 +//
 +// In Windows, a program's "Working Set" is the amount of memory the system
 +// has committed to a process. This is generally a very good measure of how
 +// much actual memory an application uses. With a measure of the amount of
 +// committed memory, I devised the following solution:
 +//    1. Measure the program's "Working Set"
 +//    2. Allocate a lot of objects.
 +//    3. Measure the "Working Set" again.
 +//    4. Divide the difference by the number of allocated objects.
 +// This method gives a rough estimate a reference type's size. It also
 +// illustrates the amount of overhead the CLR requires for allocating objects.
 +//
 +// Data
 +// Measuring Object:
 +// iter    working set     size estimate
 +// -1      11190272
 +// 1000000 85995520        74.805248
 +// 2000000 159186944       73.998336
 +// 3000000 231473152       73.4276266666667
 +// 4000000 306401280       73.802752
 +// 5000000 379092992       73.580544
 +// 6000000 451387392       73.3661866666667
 +// 7000000 524378112       73.3125485714286
 +// 8000000 600096768       73.613312
 +// 9000000 676405248       73.9127751111111
 +// Average size: 73.7577032239859
 +// Measuring LinkedListNode<Object>:
 +// iter    working set     size estimate
 +// -1      34168832
 +// 1000000 147959808       113.790976
 +// 2000000 268963840       117.397504
 +// 3000000 387796992       117.876053333333
 +// 4000000 507973632       118.4512
 +// 5000000 628379648       118.8421632
 +// 6000000 748834816       119.110997333333
 +// 7000000 869265408       119.299510857143
 +// 8000000 993509376       119.917568
 +// 9000000 1114038272      119.985493333333
 +// Average size: 118.296829561905
 +// Estimated Object size: 29.218576886067
 +// Estimated LinkedListNode<reference type> size: 44.5391263379189
 +//
 +// Analysis
 +// The average size of allocating millions of Objects is approximately 29.2
 +// bytes. A LinkedListNode<reference type> object is approximately 44.5 bytes.
 +// This data illustrates two things:
 +//    1. It's very unlikely that the system is allocating a partial byte. The
 +//       fractional measure of bytes indicates the overhead the CLR requires to
 +//       allocate and track millions of reference types.
 +//    2. If we simply round-down the number of bytes, we're still unlikely to
 +//       have the proper byte count for reference types. This is clear from the
 +//       measure of Objects. If we round down, we assume the size is 29 bytes
 +//       which, while theoretically possible, is unlikely because of padding.
 +//       In order to improve performance, object allocations are usually padded
 +//       for alignment purposes. I would guess that CLR objects will be 4 byte
 +//       aligned.
 +// Assuming CLR overhead and 4-byte alignment, I'd estimate an Object in C# is
 +// 28 bytes and a LinkedListNode<reference type> is 44 bytes.
 +//
 +
 +using System;
 +using System.Diagnostics;
 +using System.Collections.Generic;
 +
 +class CSharpSizeofReference
 +{
 +delegate void Allocator<T>(LinkedList<T> list, int iter);
 +
 +static void AllocateReferenceType<T>(LinkedList<T> list, int iter) where T : new()
 +{
 +   for (int i = 0; i < iter; i++)
 +   {
 +      list.AddLast(new T());
 +   }
 +}
 +
 +static void AllocateLinkedListNodes(LinkedList<LinkedListNode<Object>> list, int iter)
 +{
 +   for (int i = 0; i < iter; i++)
 +   {
 +      list.AddLast(new LinkedListNode<Object>(new Object()));
 +   }
 +}
 +
 +static double Measure<T>(Allocator<T> allocator)
 +{
 +   Console.WriteLine("iter\tworking set\tsize estimate");
 +
 +   GC.Collect();
 +   double workingSet = GetWorkingSet();
 +
 +   Console.WriteLine("-1\t{0}", workingSet);
 +
 +   double[] sizes = new double[9];
 +
 +   for (int i = 0; i < 9; i++)
 +   {
 +      GC.Collect();
 +
 +      int count = 1000000 * (i + 1);
 +      LinkedList<T> list = new LinkedList<T>();
 +      allocator(list, count);
 +
 +      double newWorkingSet = GetWorkingSet();
 +      double allocated = newWorkingSet - workingSet;
 +      double size = allocated / count;
 +
 +      Console.WriteLine("{0}\t{1}\t{2}", count, newWorkingSet, size);
 +      sizes[i] = size;
 +   }
 +
 +   double sum = 0;
 +   foreach (double size in sizes)
 +   {
 +      sum += size;
 +   }
 +   Console.WriteLine("Average size: {0}", (sum / 9));
 +
 +   return (sum / 9);
 +}
 +
 +static long GetWorkingSet()
 +{
 +   Process[] processes = Process.GetProcessesByName("CSharpSizeofReference");
 +
 +   if (processes.Length != 1)
 +   {
 +      throw new Exception("Expected exactly one CSharpSizeofReference process.");
 +   }
 +
 +   return processes[0].WorkingSet64;
 +}
 +
 +static int Main(String[] args)
 +{
 +   Console.WriteLine("Measuring Object:");
 +   double llobj = Measure<Object>(AllocateReferenceType<Object>);
 +
 +   Console.WriteLine("Measuring LinkedListNode<Object>:");
 +   double llllobj = Measure<LinkedListNode<Object>>(AllocateLinkedListNodes);
 +
 +   double ll = llllobj - llobj;
 +   double obj = llobj - ll;
 +
 +   Console.WriteLine("Estimated Object size: {0}", obj);
 +   Console.WriteLine("Estimated LinkedListNode<reference type> size: {0}", ll);
 +
 +   return 0;
 +}
 +}
 +</code>
random/net_sizeof_reference_type.txt · Last modified: 2011/07/12 16:51 by grant