4

I know for a C# main program the stack size 1 MB (32-bit and any) or 4 MB (64-bit), see Why is stack size in C# exactly 1 MB?

What is the default stack size of the BackgroundWorker DoWork thread?

Is there a way to change the stack size of the BackgroundWorker DoWork thread beside creating another thread like the following example:

private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{   
    Thread thread = new Thread(delegate()
    {
        // do work with larger stack size
    }, 8192 * 1024);
    thread.Start();
    thread.Join();
}

I'm using a BackgroundWorker because I've a Windows Forms application where I do some calculations inside the DoWork event. I'm doing it this way because I want to report back to the status line of the GUI and I want the possibility that user can cancel the calculations.

I'm getting a stack overflow error because I'm calling Intel MKLs LAPACKE_dtrtri which is heavily recursive, see http://www.netlib.org/lapack/explore-html/df/d5c/lapacke__dtrtri_8c_source.html.

The following code shows how I call Intel MKL:

public static double[,] InvTriangularMatrix(double[,] a, bool isupper)
{
    int n1 = a.GetLength(0);
    int n2 = a.GetLength(1);
    if (n1 != n2) throw new System.Exception("Matrix must be square");
    double[,] b = Copy(a);
    int matrix_layout = 101; // row-major arrays
    char uplo = isupper ? 'U' : 'L';
    char diag = 'N';
    int lda = Math.Max(1, n1);
    int info = _mkl.LAPACKE_dtrtri(matrix_layout, uplo, diag, n1, b, lda);
    if (info > 0) throw new System.Exception("The " + info + "-th diagonal element of A is zero, A is singular, and the inversion could not be completed");
    if (info < 0) throw new System.Exception("Parameter " + (-info) + " had an illegal value");
    return b;
}

and

[DllImport(DLLName, CallingConvention = CallingConvention.Cdecl, ExactSpelling = true, SetLastError = false)]
internal static extern int LAPACKE_dtrtri(
    int matrix_layout, char uplo, char diag, lapack_int n, [In, Out] double[,] a, int lda);

The InvTriangularMatrix is called inside my DoWork event. When I'm not setting the stack size I'm getting a stack overflow error inside the LAPACKE_dtrtri function.

The size of matrix can be in the order of 1000 x 1000 up to 100000 x 100000. If the matrix is larger than 65535 x 65535 see 2d-Array with more than 65535^2 elements --> Array dimensions exceeded supported range.

Wollmich
  • 1,616
  • 1
  • 18
  • 46
  • 7
    BGW uses a threadpool thread, you can't change their stack size. Do be careful with this kind of workaround, this is in general only needed to fix a problem with recursive code. If it is a problem while testing then it rarely gets better in production when it needs to process real datasets. – Hans Passant Jun 05 '19 at 09:29
  • 1
    If you find yourself having to increase stack size, then probably what you should really be doing is converting a recursive method into an iterative one that is implemented using `Stack`. – Matthew Watson Jun 05 '19 at 09:41
  • If you're creating threads in DoWork, then a BackgroundWorker is not what your want. – Andy Jun 05 '19 at 10:39
  • Why are you starting *another background * thread inside BGW's background thread? In any case BGW is obsolete, fully replaced by `Task.Run` and `IProgress`. – Panagiotis Kanavos Jun 05 '19 at 11:11
  • @HansPassant What is the stack size of threadpool thread? I'm using this workaround to call Intel MKLs LAPACKE_dtrtri which is heavily recursive. – Wollmich Jun 05 '19 at 11:12
  • @Wollmich the correct question should be "why should you care"? If you get a stack overflow error it means you have a bug in your recursion code. Arrays aren't allocated on the stack unless you use `stackalloc` – Panagiotis Kanavos Jun 05 '19 at 11:13
  • @PanagiotisKanavos I'm using the `BackgroundWorker` because it has the `ReportProgress` and `Cancel` functionalities. I'm starting another thread because I've to increase the stack size. – Wollmich Jun 05 '19 at 11:15
  • @Wollmich BGW is meant to perform work on the background, not report progress. Again, that's obsolete, replaced by the `IProgress` interface. And you don't need to increase the stack size unless you explicitly try to allocate a big array with `stackalloc`. In any other case, there's a bug in the recursion code. – Panagiotis Kanavos Jun 05 '19 at 11:16
  • @PanagiotisKanavos It's not my recursion code. I'm calling Intel MKLs LAPACKE_dtrtri. – Wollmich Jun 05 '19 at 11:16
  • Post your code then. *How* do you call that method? Using which libraries? Where did you get the error? Don't try to cover it up - the *stack* isn't general-purpose memory, even in C++ and native code – Panagiotis Kanavos Jun 05 '19 at 11:17
  • @PanagiotisKanavos. Intel MKL 2019 Update 2, `dllimport`, I'm getting the stack overflow error inside the LAPACKE_dtrtri function. – Wollmich Jun 05 '19 at 11:20
  • 2
    It is formally unspecified since it depends on the CLR host, if you target Windows then it is a megabyte in any flavor. It will never be enough to keep you happy. Do shop around a bit, matrix inversion isn't rocket science. Try Math.NET. – Hans Passant Jun 05 '19 at 11:39
  • @HansPassant Math.NET is using as well IntelMKL. If you switch to manged it gets to slower for larger problems. – Wollmich Jun 05 '19 at 11:52

1 Answers1

2

The stack size inside a BackgroundWorker DoWork event is the same as for the main thread.

Prof:

Set the stack size in a post build event to 8 MB for example:

"$(DevEnvDir)..\..\VC\bin\editbin.exe" /STACK:8388608 "$(TargetPath)"

Then ask for the stack size using the following code:

[DllImport("kernel32.dll")]
internal static extern void GetCurrentThreadStackLimits(out uint lowLimit, out uint highLimit);


public static uint GetStackSize()
{
    uint low, high;
    GetCurrentThreadStackLimits(out low, out high);
    return high - low;
}

Using GetStackSize in the main program and in the DoWork event return in both cases 8 MB or whatever you specified using EDITBIN /STACK.

Wollmich
  • 1,616
  • 1
  • 18
  • 46
  • This works only when you start without debugging otherwise it's 1 MB. – Wollmich Jun 06 '19 at 13:02
  • So in debug-mode you will get still for x86 = 1MB and for x64 = 4MB right? atleast for release you get more than . – Shpendicus Apr 15 '23 at 20:31
  • Using Visual Studio 2019 you will get your specified stack size even in debug mode. When I wrote the above comment I was still using Visual Studio 2013. – Wollmich Apr 17 '23 at 07:22