1

I am trying to implement a Task scheduler where i have n number of tasks. The Idea behind my task scheduler is that in a loop of queues of a vector, task should get enqueued to the shortest queue among the loop of queues, which is done by the following code.

#include <vector> 
#include <queue> 
std::vector<std::queue<int> > q
int min_index = 0;
task t // implemented in the other part of the program
std::size_t size = q.size();
for( i=0; i<size; i++){ //accessing loop of queues
    if(q[min_index].size() > q[i].size())
        min_index = i; // Now q[min_index] is the shortest queue
} 
q[min_index].push(Task);

Next i am trying to extend this paradigm to reduce the overhead time of the scheduler, Instead of searching the shortest queue every time, search after some condition ie. search the shortest queue after 5 tasks gets enqueued to the shortest queue.

i need to do something like this

#include <vector> 
#include <queue> 
std::vector<std::queue<int> > q
task t // implemented in the other part of the program
while(q[min_index].size()!=q[min_index].size()+5) // check whether current min_index  queue's size is increased 5 more times if not goto enqueue
        {
        goto enqueue;
        }

    int min_index = 0;

    std::size_t size = q.size();
    for( i=0; i<size; i++){ //accessing loop of queues
        if(q[min_index].size() > q[i].size())
            min_index = i; // Now q[min_index] is the shortest queue
    } 
    enqueue:  
    q[min_index].push(Task);

can someone please help me how to proceed it correctly. thanks in advance

Updated Instead of having 5 ( a random number) i thought of having a number which is reliable and approximate everytime. so i thought of get the difference of min_value size and max_value size for the loop of queues and compare it with the counter each time.

// global variables 
std::vector<std::queue<int> > q;
int counter = INT_MAX; // 
int min_index = 0;
int max_size = -1;

void enqueue(scheduler::task new_task) { 
 if ( counter > diff_size ){
  // look for new min and maximum
  std::size_t size = q.size();
  for( i=0; i<size; i++){ 
    if(q[min_index].size() > q[i].size())
     min_index = i; 
       if(q[i].size() > max_size)
        max_size = q[i].size(); 
    diff_size=max_size - min_index;
  } 
  // counter reset
  counter = 0;
 }

 // enqueue in minimum queue
 q[min_index].push(new_task)

 // increase counter
 counter ++;
}
billa
  • 95
  • 7
  • Generally speaking, you should just use a single queue. Multiple queues almost always add overhead *and* result in less-optimal scheduling. – Jerry Coffin Mar 07 '13 at 14:17
  • @JerryCoffin yes but a famous scheduling technique called 'work stealing' uses local queues to threads, so i am just trying to use the same idea in another way. – billa Mar 07 '13 at 14:19
  • Well, if you insist on doing it, the best way is probably to maintain a priority queue of your queues, with the priority based on their lengths, so you always have easy access to the shortest queue. – Jerry Coffin Mar 07 '13 at 14:22
  • @JerryCoffin Ok but i tried this one i got a significant reduction in overhead time rather than using a single global queue but still slower than the 'work stealing'. so i am to reduce the overhead by this new update. could you please help me how to proceed the updated part correctly – billa Mar 07 '13 at 14:29
  • So you have used the priority queue (i.e. heap, but C++ does call it priority_queue) of the queues? And if the other implementation you are comparing with is still faster, can you have a look at how it is implemented? – Jan Hudec Mar 07 '13 at 14:31
  • @JanHudec In work stealing technique, they used local queues per thread,where when a thread is out of work, it steals work from the busy threads. – billa Mar 07 '13 at 14:36
  • @JanHudec Could you please help me out the logic alone how to proceed the update technique.ie,.check whether current min_index queue's size is increased 5 more times if not goto enqueue – billa Mar 07 '13 at 14:41
  • Implementations of [calendar queues](http://stackoverflow.com/questions/6004978/what-is-a-calendar-queue) often use multiple queues in this way. – Richard Mar 07 '13 at 14:42
  • 1
    So, @billa, you want to find the shortest queue, add the next 5 tasks to it, and then do the search again? This way you don't have to search every time. Is that what you want? – Richard Mar 07 '13 at 14:43
  • Give me a moment, @billa. – Richard Mar 07 '13 at 14:44
  • @RichardSure take your time – billa Mar 07 '13 at 14:45
  • How many queues were you wanting? – Richard Mar 07 '13 at 14:45
  • at some point, the shortest queue will no more be the shortest right? how do you wait for 5 tasks to be added to that same queue? If the distance between the queues is very less, this might lead to pretty long waits with no yield. Hope my thought process is correct - do correct me if I am thinking wrong. – Rakesh K Mar 07 '13 at 14:46
  • @RichardI dont know, n number of queues depending on the number of threads, Thats why i made the queues in a vector, you can just refer my first code in the question where i found the shortest queue in a loop of n queues in a vector. – billa Mar 07 '13 at 14:48
  • @RakeshKYes "at some point, the shortest queue will no more be the shortest", your guess is correct. But this is just a trial and error iteration to find which technique 'either to search every time' or 'search after some condition' gives low overhead. – billa Mar 07 '13 at 14:51
  • probably you would reduce more overhead if you searched after every n times where n=length of min_queue. – Rakesh K Mar 07 '13 at 14:58
  • @RakeshKYes that would be more clever instead of choosing a random number.But could you please help me how to do it in programming point of view.I just found the shortest queue in the above code among the loop of queues. – billa Mar 07 '13 at 15:04

1 Answers1

1

I guess I would try this (the code does compile, and it does work):

#include <vector> 
#include <queue>
#include <cassert>
#include <iostream>
using namespace std;

class multi_queue{
  private:
    typedef std::vector<std::queue<int> > mq_type;
    mq_type q;
    int min_queue;
    int min_queue_inc;
    int max_inc;
    void find_min_queue(){
      max_inc=q[min_queue].size();
      min_queue_inc=0;
      min_queue=0;
      for(int i=1;i<q.size();++i)
        if(q[i].size()<q[min_queue].size())
          min_queue=i;
    }
  public:
    multi_queue(int n) {
      assert(n>0);
      min_queue=0;
      min_queue_inc=0;
      max_inc=5;
      increase_queues(n);
    }
    void increase_queues(int n){ 
      if(n<q.size()) return;
      q.resize(n);
    }
    void push(int item){
      if(min_queue_inc++==max_inc)
        find_min_queue();
      q[min_queue].push(item);
    }
    int queues() const {
      return q.size();
    }
    int pop_from(int qnum){
      assert(qnum>=0);
      assert(qnum<q.size());
      assert(can_pop_from(qnum));
      int temp=q[qnum].front();
      q[qnum].pop();
      return temp;
    }
    bool can_pop_from(int qnum) const {
      return q[qnum].size()>0;
    }
    int largest_queue() const {
      int largest=0;
      for(int i=1;i<q.size();++i)
        if(q[i].size()>q[largest].size())
          largest=i;
      return q[largest].size();
    }
};


int main(){
  multi_queue q(10);
  for(int i=0;i<100;++i){
    cout<<"Current largest queue: "<<q.largest_queue()<<endl;
    cout<<"Pushing: " <<i<<endl;
    q.push(i);
  }

  for(int i=0;i<10;++i)
  for(int j=0;j<100;++j)
    if(q.can_pop_from(i))
      cout<<q.pop_from(i)<<endl;
}

Naturally you would not want to display q.largest_queue() each time because that would defeat the point, but I do it here so that you will be able to see that things are working.

The important methods from the stand-point of answering your question are push() and find_min_queue().

I use two state variables, min_queue and min_queue_inc to keep track of what's going on.

min_queue always points to the shortest queue. min_queue_inc keeps track of how many items have been added to that queue.

In push() we check to see whether the current minimum queue has 5 items added to it; if so, it is time to see whether there is a new minimum queue we should be using, and we therefore call find_min_queue().

find_min_queue() resets min_queue_inc, finds the shortest queue, and makes a note of that queue in min_queue.

Depending on what you want to do, adjust the behavior of the variable max_inc to suit your needs.

Richard
  • 56,349
  • 34
  • 180
  • 251
  • @richardif i want to increase the size depending the length on the minimum queue instead of 5. what should i change? – billa Mar 07 '13 at 15:22
  • I don't follow what you're asking, @billa. – Richard Mar 07 '13 at 15:46
  • @RichardHere we increased min queue's size until 5 and then the started the next search operation right? what if i need to increase the min queue's size until n where n is the 'length of minimum queue' – billa Mar 07 '13 at 15:52
  • @billa, I still don't follow. You say that you want to increase the min queue's size until it is the length of the minimum queue? That's true by definition. Perhaps you want to increase the minimum queue until it's the length of the maximum? But how will you know how long to make the maximum queue to begin with? – Richard Mar 07 '13 at 15:55
  • @RichardInstead of adding 5 tasks to the shortest queue after finding the shortest queue, i need the add n number of tasks = size of minimum queue. (ie. if shortest queue size is 3 , add 3 tasks. if it is 7, add 7 tasks). is it clear now. sorry i am confusing you – billa Mar 07 '13 at 16:02
  • @billa, are you wanting to make all queues size 1, then size 2, then size 3? Or are you somehow starting your code with items on the queue already? – Richard Mar 07 '13 at 16:08
  • @billa, I've edited to the code to max the place where you change the target queue increment more apparent. – Richard Mar 07 '13 at 16:10
  • @RichardOh yes that's what i meant, i can change the minimum queue increment more apparent – billa Mar 07 '13 at 16:13
  • 1
    So this works for you, @billa? Note also that I've added some functions related to removing elements from the multi-queue. – Richard Mar 07 '13 at 16:13
  • @richardYes it compiles and works, Thanks much.And Is it possible to do the same task without the functions and class, just with the ‘statements and loops’ as i did the code in my question. Because this part of i need to add within a function Enqueue() of my scheduler .hpp file. Adding a class and functions would increase the complexity and i need to change all realted .hpp files. – billa Mar 07 '13 at 16:17
  • 1
    @billa, you can simply tack the class into your file and use a [static variable](https://en.wikipedia.org/wiki/Static_variable) to instantiate it. Otherwise, all the code is here, you should be able to split it up without too much difficulty. However, I'd recommend that you'd try to find a way to do this with classes, or this class, since it provides good encapsulation. – Richard Mar 07 '13 at 16:22
  • let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/25789/discussion-between-billa-and-richard) – billa Mar 07 '13 at 16:27
  • @RichardI have tried out with a counter and global variables, please see the updated part in my question and let me know whether it can be done like that. – billa Mar 08 '13 at 09:39
  • @Richardcould you please check out the updated code part in my question and let me know whether it can be proceeded like that. – billa Mar 08 '13 at 14:35