Home > Barry > Thanks for the Memory

Thanks for the Memory

January 23rd, 2009

When retro-fitting a sequential application with threads, there are many potential problems however its always the side effects that seem to get you with unexpected bugs. This situation becomes worse when you are attempting to crowbar your application into the resource constrained environment of an embedded system.

Threads, here I’m talking about PThreads but it is true for most libraries, have overhead. There is design overhead, code , maintenance and runtime overheads and of course memory overhead which is the one you really have to lookout for.

PThreads are basically lightweight processes and so have to carry around a good deal of state with them and chief among these is the stack. In a typical implementation, the stack for each thread is allocated on the heap when the thread is created and is a fixed size. This sounds straightforward enough but can immediately cause several failure modes.

  • The per thread stack size X the number of threads is greater than the available heap space resulting in thread creation failure.
  • There is enough space on the heap for all of thread overhead but the next malloc call fails because there is no more room.
  • The per thread stack is too small for the amount of data that needs to be pushed onto it, leading to any number of obscure errors.

So what can you do about this? Fortunately PThreads provides a solution via its thread attributes API. If you are not familiar with attributes it is simply a structure for configuring a thread which is passed to pthread_create as the second parameter which is otherwise set to NULL, as below.


pthread_attr_t myAttr;
pthread_attr_init(&myAttr);
pthread_create(&aThread, &myAttr, (void *) myFunc, (void *) myParams);

Once the pthread_attr_t variable is initialized you can use it to specify the size of the stack you require (among other things). However it is not always that straightforward (is anything) and you will need to test if your PThreads library actually supports this variable stack size and then you need to test what the minimum allowable stack size is before you set it. This is handled via declarations for your system.


pthread_attr_t myAttr;
pthread_attr_init(&myAttr);
size_t myStackSize;
#ifdef _POSIX_THREAD_ATTR_STACKSIZE
pthread_attr_getstacksize(&myAttr, &myStackSize);
printf(“Default Stack is %d Bytes.\n”, myStackSize);
myStackSize = 16*1024; “// Try to set stack to 16K”
if (myStackSize >= PTHREAD_STACK_MIN)
pthread_attr_setstacksize(&myAttr, myStackSize);
else
printf(“PANIC! New Stack size too small!.\n”);
#else
printf(“PANIC! Cannot set stacksize.\n”);
#endif

So there you go. Each thread can now have a custom stack size tailored to its own needs, minimizing the total thread overhead of the system. Of course the problems don’t end there. Many modifications when parallelizing serial code, particularly when attempting to improve performance by adding thread local buffers, will further increase the memory footprint of your application and unexpected combinations of threads may result in memory usage spikes. So on behalf of future code maintainers everywhere please, please check the return value of malloc() and friends for failures!

Barry

  1. No comments yet.
  1. No trackbacks yet.