2

I am running a multi-threaded program on my computer which has 4 cores. I am creating threads that run with SCHED_FIFO, SCHED_OTHER, and SCHED_RR priorities. What is the maximum number of each type of thread that can run simultaneously?

For example, I'm pretty sure only four SCHED_FIFO threads can run at a time (one per core) but I'm not sure about the other two.

edit my code, as asked (it's long, but most of it is for testing how long each thread completes a delay task)

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
#include <sys/time.h>
#include <time.h>
#include <string.h>

void *ThreadRunner(void *vargp);
void DisplayThreadSchdStats(void);
void delayTask(void);

int threadNumber = 0;
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;

#define NUM_THREADS    9

//used to store the information of each thread
typedef struct{
    pthread_t  threadID;
    int policy;
    struct sched_param param;
    long startTime;
    long taskStartTime;
    long endTime1;
    long endTime2;
    long endTime3;
    long runTime;
    char startDate[30];
    char endDate[30];
}ThreadInfo;

ThreadInfo myThreadInfo[NUM_THREADS];



//main function
int main(void){

   printf("running...\n");

   int fifoPri = 60;
   int rrPri = 30;

   //create the 9 threads and assign their scheduling policies
   for(int i=0; i<NUM_THREADS; i++){

       if(i%3 == SCHED_OTHER){
            myThreadInfo[i].policy = SCHED_OTHER;
            myThreadInfo[i].param.sched_priority = 0;
       }
       else if (i%3 == SCHED_FIFO){ 
            myThreadInfo[i].policy = SCHED_RR;
            myThreadInfo[i].param.sched_priority = rrPri++; 
       }
       else{
            myThreadInfo[i].policy = SCHED_FIFO; 
            myThreadInfo[i].param.sched_priority = fifoPri++; 
       }
       pthread_create( &myThreadInfo[i].threadID, NULL, ThreadRunner, &myThreadInfo[i]);
pthread_cond_wait(&cond, &mutex);
   }

   printf("\n\n");


   //join each thread
   for(int g = 0; g < NUM_THREADS; g++){
      pthread_join(myThreadInfo[g].threadID, NULL);
   }


   //print out the stats for each thread
   DisplayThreadSchdStats();


   return 0;
}


//used to print out all of the threads, along with their stats
void DisplayThreadSchdStats(void){

   int otherNum = 0;
   long task1RR = 0;
   long task2RR = 0;
   long task3RR = 0;
   long task1FIFO = 0;
   long task2FIFO = 0;
   long task3FIFO = 0;
   long task1OTHER = 0;
   long task2OTHER = 0;
   long task3OTHER = 0;

   for(int g = 0; g < threadNumber; g++){

      printf("\nThread# [%d]  id [0x%x] exiting...\n", g + 1, (int) myThreadInfo[g].threadID);
      printf("DisplayThreadSchdStats:\n");
      printf("   threadID    = 0x%x \n", (int) myThreadInfo[g].threadID);

      if(myThreadInfo[g].policy == 0){
         printf("   policy      = SHED_OTHER\n");
         task1OTHER += (myThreadInfo[g].endTime1 - myThreadInfo[g].taskStartTime); 
         task2OTHER += (myThreadInfo[g].endTime2 - myThreadInfo[g].endTime1);
         task3OTHER += (myThreadInfo[g].endTime3 - myThreadInfo[g].endTime2);
         otherNum++;
      }
      if(myThreadInfo[g].policy == 1){
         printf("   policy      = SHED_FIFO\n");
         task1FIFO += (myThreadInfo[g].endTime1 - myThreadInfo[g].taskStartTime); 
         task2FIFO += (myThreadInfo[g].endTime2 - myThreadInfo[g].endTime1);
         task3FIFO += (myThreadInfo[g].endTime3 - myThreadInfo[g].endTime2);

      }
      if(myThreadInfo[g].policy == 2){
         printf("   policy      = SHED_RR\n");
         task1RR+= (myThreadInfo[g].endTime1 - myThreadInfo[g].taskStartTime); 
         task2RR += (myThreadInfo[g].endTime2 - myThreadInfo[g].endTime1);
         task3RR += (myThreadInfo[g].endTime3 - myThreadInfo[g].endTime2);

      }



      printf("   priority    = %d \n", myThreadInfo[g].param.sched_priority);
      printf("   startTime   = %s\n", myThreadInfo[g].startDate);
      printf("   endTime     = %s\n", myThreadInfo[g].endDate);
      printf("   Task start TimeStamp in micro seconds [%ld]\n", myThreadInfo[g].taskStartTime);
      printf("   Task end   TimeStamp in micro seconds [%ld] Delta [%lu]us\n", myThreadInfo[g].endTime1 , (myThreadInfo[g].endTime1 - myThreadInfo[g].taskStartTime));
      printf("   Task end   Timestamp in micro seconds [%ld] Delta [%lu]us\n", myThreadInfo[g].endTime2, (myThreadInfo[g].endTime2 - myThreadInfo[g].endTime1));
      printf("   Task end   Timestamp in micro seconds [%ld] Delta [%lu]us\n\n\n", myThreadInfo[g].endTime3, (myThreadInfo[g].endTime3 - myThreadInfo[g].endTime2));
      printf("\n\n");


   }

   printf("Analysis: \n");
   printf("  for SCHED_OTHER, task 1 took %lu,  task2 took %lu,  and task 3 took %lu.   (average = %lu)\n", (task1OTHER/otherNum), (task2OTHER/otherNum), (task3OTHER/otherNum), (task1OTHER/otherNum + task2OTHER/otherNum + task3OTHER/otherNum)/3 );
   printf("  for SCHED_RR,    task 1 took %lu,  task2 took %lu,  and task 3 took %lu.   (average = %lu)\n", (task1RR/otherNum), (task2RR/otherNum), (task3RR/otherNum), (task1RR/otherNum + task2RR/otherNum + task3RR/otherNum)/3 );
   printf("  for SCHED_FIFO,  task 1 took %lu,  task2 took %lu,  and task 3 took %lu.   (average = %lu)\n", (task1FIFO/otherNum), (task2FIFO/otherNum), (task3FIFO/otherNum) , (task1FIFO/otherNum + task2FIFO/otherNum + task3FIFO/otherNum)/3);

}





//the function that runs the threads
void *ThreadRunner(void *vargp){


   pthread_mutex_lock(&mutex);


   char date[30];
   struct tm *ts;
   size_t last;
   time_t timestamp = time(NULL);
   ts = localtime(&timestamp);
   last = strftime(date, 30, "%c", ts);
   threadNumber++;
   ThreadInfo* currentThread;
   currentThread = (ThreadInfo*)vargp;


   //set the start time
   struct timeval  tv;
   gettimeofday(&tv, NULL);
   long milltime0 = (tv.tv_sec) * 1000 + (tv.tv_usec) / 1000;
   currentThread->startTime = milltime0;

   //set the start date
   strcpy(currentThread->startDate, date);

   if(pthread_setschedparam(pthread_self(), currentThread->policy,(const struct sched_param *) &(currentThread->param))){
      perror("pthread_setschedparam failed");
      pthread_exit(NULL);
   }

   if(pthread_getschedparam(pthread_self(), &currentThread->policy,(struct sched_param *) &currentThread->param)){
      perror("pthread_getschedparam failed");
      pthread_exit(NULL);
   }

   gettimeofday(&tv, NULL);
   long startTime = (tv.tv_sec) * 1000 + (tv.tv_usec) / 1000;
   currentThread->taskStartTime = startTime;
   //delay task #1
   delayTask();

   //set the end time of task 1
   gettimeofday(&tv, NULL);
   long milltime1 = (tv.tv_sec) * 1000 + (tv.tv_usec) / 1000;
   currentThread->endTime1 = milltime1;


   //delay task #2
   delayTask();

   //set the end time of task 2
   gettimeofday(&tv, NULL);
   long milltime2 = (tv.tv_sec) * 1000 + (tv.tv_usec) / 1000;
   currentThread->endTime2 = milltime2;


   //delay task #3
   delayTask();

   //set the end time of task 3
   gettimeofday(&tv, NULL);
   long milltime3 = (tv.tv_sec) * 1000 + (tv.tv_usec) / 1000;
   currentThread->endTime3 = milltime3;


   //set the end date
   timestamp = time(NULL);
   ts = localtime(&timestamp);
   last = strftime(date, 30, "%c", ts);
   strcpy(currentThread->endDate, date);


   //set the total run time of the thread
   long runTime = milltime3 - milltime0;
   currentThread->runTime = runTime;

   //unlock mutex
   pthread_mutex_unlock(&mutex);
   pthread_cond_signal(&cond);

   pthread_exit(NULL);
}

//used to delay each thread
void delayTask(void){
   for(int i = 0; i < 5000000; i++){
      printf("%d", i % 2);
   }

}
Lazarus
  • 125
  • 2
  • 15
  • do you have any code. – Jacob Feb 10 '18 at 17:57
  • I added my code. – Lazarus Feb 10 '18 at 18:01
  • for all intensive purposes each core executes one thread at a time. But the operating system schedules the processes. A process shouldn't be able to access the scheduler directly. – Jacob Feb 10 '18 at 18:24
  • So basically my computer could run 4 threads of each scheduling priority at any given time (or a mix of up to four of them)? And the different scheduling priorities just dictate which ones fire off when. – Lazarus Feb 10 '18 at 18:31

1 Answers1

4

In short: no guarantees how many threads will be run parallelly, but all of them will run concurrently.

No matter how many threads you start in an application controlled by a general-purpose operating system, they all will run concurrently. That is, each thread will be provided with some non-zero time to run, and no particular execution order of execution of threads' sections outside OS-defined synchronization primitives (waiting on mutexes, locks etc.) is guaranteed. The only limit on thread number may be imposed by OS'es policies.

How many of your threads will be chosen to run parallelly at any given moment of time is not defined. The number cannot obviously exceed number of logical processors visible to an OS (remember that the OS itself may be run inside a virtual machine, and there are hardware tricks like SMT), and your threads will be competing with other threads present in the same system. OSes do offer APIs to query which threads/processes are currently in running state and which are blocked or ready but not scheduled, otherwise writing programs like top would become problematic.

Explicitly setting priorities to threads may affect the operating system's choices and increase average number of your threads being executed parallelly. Note that it can either help or hurt if used without thinking. Still, it will never be strictly equal to four inside a multitasking OS as long as there are other processes. The only way to make sure 100% of CPU's hardware is dedicated to your threads 100% of the time is to run a barebone application, outside of any OS outside of any hypervisor (and even then there are peculiarities, see "Intel System Management Mode").

Inside a mostly idle general purpose OS, if your threads are compute-intensive, I would guess the average parallel utilization ratio would be 3.9 — 4.0. But a slightest perturbation — and all bets are off.

Grigory Rechistov
  • 2,104
  • 16
  • 25