In the previous article I discussed a few of the benefits of stack allocation as well as a couple of C# keywords which help you to leverage those benefits. However, the one megabyte default stack size is too small for stack allocation to be used with a large dataset. Alternatively, in some threading situations one megabyte per thread/fiber can be too large and bottleneck your system. In this article I will discuss the different ways you can modify the stack size.

 

Articles in This Series

Part 1 – Basic Housekeeping
Part 2 – Improving Performance Through Stack Allocation

Part 3 – Increasing the Size of your Stack
Part 4 – Choosing the Right Garbage Collector Settings

Part 5
– Changing Your Garbage Collector Settings on the Fly

 

Why Not To Increase Your Stack Size

There are many cases in which it is best to not to increase your stack size. In fact right inside the Microsoft documentation for the Thread Constructor it states:

If a thread has memory problems, the most likely cause is programming error, such as infinite recursion.

And the Thread Stack Size operating system documentation gives this advice:

It is best to choose as small a stack size as possible and commit the stack that is needed for the thread or fiber to run reliably. Every page that is reserved for the stack cannot be used for any other purpose.

This is all generally good advice to follow. However, there are some cases in which it may be appropriate or even necessary to change your stack size.

 

Why You Might Want To Modify Your Stack Size

Scenario 1: Many Threads

If you are in a situation where you need to create a great number of threads (or fibers) each will require its own stack. In this case each of those threads having a large stack size can eat up a ton of memory. By decreasing your stack size it is possible to accommodate a much larger number of threads.

Scenario 2: Optimization

You may want to utilize the convenience and speed of stack allocation. Some might see this as poor design but many an ugly hack has been made in the name of performance.

Other Scenarios

Obviously, there are other scenarios where stack size modification could be helpful. I would love to hear about your personal experience with it.

 

Stack Size Modification Techniques

In C++ you can simply specify the linker’s /stack option but in C# you have to jump through a few hoops in order to change stack size.

 

The Easiest Way ( .NET 2.0 )

In .NET 2.0 and newer you can simply specify thread size in a thread’s constructor. Unfortunately, this method is only compatible only with Windows XP and newer operating systems. You can specify this parameter on those platforms but it will have no effect; the stack size in the binary header will be used.

using System.Threading;

Thread T = new Thread(threadDelegate, stackSizeInBytes);
T.Start();

 
Pros:

  • Very Easy
  • Can Dynamically Specify Thread Size at Creation Time

 

Cons:

  • Only Available in .NET 2.0 and Above
  • Stack Size Parameter Ignored in Pre-XP Operating Systems

 

The Old Way ( .NET 1.x )

In .NET 1.x the only option is to programmatically specify thread size is to PInvoke into kernel32.dll and execute CreateThread. This method also has the advantage of being extremely backwards compatible. It’s not pretty, but it gets the job done.

 

using System.Runtime.InteropServices;

unsafe
class Kernel32Thread

[DllImport("kernel32.dll")]
static
extern IntPtr CreateThread(...

hThread = CreateThread( IntPtr.Zero, stackSizeInBytes, threadDelegate, pArguments, 0, out threadId );

WaitForSingleObject( hThread, timeout );
CloseHandle( hThread );

 

This is only a general overview of what is necessary. The complete code needed is fairly large, so I have attached it as a separate file.

The MSDN documentation specifies that this will be backwards compatible to Windows 2000. However, kernel32.dll supported specifying the stack size all the way back to Win95 and NT 3.1.

 

Pros:

  • Backwards Compatible to Windows 95
  • Can Dynamically Specify Thread Size at Creation Time
  • .NET 1.x Support

 

Cons:

  • Unsafe
  • External Calls to kernel32.dll
  • Difficult

 

Links:

If you are interested, you can learn about creating a thread in another process in an article on Mike Stall’s blog.

Maxim Alekseyken has a Code Project article which describes running a thread directly from inline byte code.

 

The Static Way ( External Utility )

The last option is to use an external utility to modify the binary executable’s header. Visual Studio comes with a tool for this task and it is very simple to use:

            EDITBIN.EXE /STACK:reserve[,commit] <files>

Where reserve is the maximum memory to allocate for stack the commit value depends on your operating system:

The optional commit argument is subject to interpretation by the operating system. In Windows NT, Windows 95, and Windows 98, commit specifies the amount of physical memory to allocate at a time.

An example use would be:

            EDITBIN.EXE /STACK:131072 file.exe

In my opinion, it’s best to do stack size changes in the code if at all possible. Using a command line utility, even if it’s in the post build event, is not always obvious and could be easily overlooked. a StackOverflowException will be thrown if you try to use more memory than is available in your stack.

 

Pros:

  • Very Easy
  • Backwards Compatible to Windows 95

 

Cons:

  • No Dynamically Sized Stacks
  • Not Part of the Code and So Easy To Forget About

 

Misc Extra Info on Stack Size Modification

From The Thread Stack Size section of the MSDN Win32 Development Documentation:

  • The default size for the reserved and initially committed stack memory is specified in the executable file header.
  • Thread or fiber creation fails if there is not enough memory to reserve or commit the number of bytes requested.
  • The operating system rounds up the specified size to the nearest multiple of the system's allocation granularity (typically 64 KB).