Welcome to CSharp Labs

Realizing stackalloc's Potential with Large Arrays

Saturday, May 18, 2013

The stackalloc keyword is used to allocate a block of memory directly on the stack. This can produce significant performance gains. There is one major caveat: .NET threads have a default stack size of 1MB. Using an stackalloc array with a large number of elements can easily cause a StackOverflowException.

To realize stackalloc's potential with large arrays, we can initialize a separate worker thread with a stack size. The new stack size should be a sum of the expected total maximum data size and the default stack size of 1MB in bytes.

        private static void ModifiedStack()
        {
            //10 million elements:
            const int DATA_SIZE = 10000000;
            //Calculate data size:
            const int EXPECTED_MAXIMUM_DATA_SIZE = sizeof(int) * DATA_SIZE;
            //New Thread receives a stack size of 1MB + expected maximum data size.
            const int NEW_THREAD_STACK_SIZE = 1048576 + EXPECTED_MAXIMUM_DATA_SIZE;

            //Initialize the Thread with maxStackSize = NEW_THREAD_STACK_SIZE
            System.Threading.Thread worker = new System.Threading.Thread(() =>
            {
                unsafe
                {
                    int* c = stackalloc int[DATA_SIZE];
                }
            }, maxStackSize: NEW_THREAD_STACK_SIZE);

            //Start the Thread and block the calling Thread
            worker.Start();
            worker.Join();
        }

Alternatively, I have created an extension method in a static class to improve usability:

        /// <summary>
        /// Joins the Thread with an Action run on a new Thread. 
        /// Blocks the calling thread until the Action completes.        
        /// </summary>
        /// <param name="thread">The Thread to block.</param>
        /// <param name="del">A method to call on the new Thread.</param>
        /// <param name="expectedDataSize">The total expected size of data 
        /// that will be placed on the stack.</param>
        public static void Join(this System.Threading.Thread thread, System.Action del, 
int expectedDataSize)
        {
            if (expectedDataSize < 0)
                throw new ArgumentException("expectedDataSize must be greater than or equal to 0.", "expectedDataSize");

            //Initialize the Thread with maxStackSize = 1MB + expectedDataSize
            System.Threading.Thread worker = new System.Threading.Thread(() => del(), maxStackSize: 1048576 + expectedDataSize);
            
            //Start the Thread and block the calling Thread
            worker.Start();
            worker.Join();
        }

The extension method can be used by calling the Join method of a Thread with a method and expected maximum data size.

        private static void ExtensionMethodModifiedStack()
        {
            //Create 10 Million Elements:
            const int DATA_SIZE = 10000000;
            //Calculate data size:
            const int EXPECTED_MAXIMUM_DATA_SIZE = sizeof(int) * DATA_SIZE;

            System.Threading.Thread.CurrentThread.Join(() =>
            {
                unsafe
                {
                    int* c = stackalloc int[DATA_SIZE];
                }
            }, expectedDataSize: EXPECTED_MAXIMUM_DATA_SIZE);
        }
Comments