2

Let's assume I have n jobs and m machines. The jobs have precedence constraints (given in a directed, acyclic graph) and different execution times. The schedule must NOT be preemptive. What's the best algorithm to schedule them? Any suggestions? I know it's NP-hard in general, so heuristics are also okay. I would consider Hu Level Scheduling as given here http://web.cecs.pdx.edu/~mperkows/temp/0002.scheduling2.pdf but if I understand it right, it assumes equal execution times.

user3666197
  • 1
  • 6
  • 50
  • 92
Flontis
  • 307
  • 1
  • 4
  • 13

2 Answers2

1

I would suggest a greedy heuristic.

Suppose that your jobs have an execution_time and children. Let the dependency_time be execution_time for leaf jobs, and execution_time + max(dependency_time for children) for other jobs.

At every step, schedule the available job with the largest dependency_time.

btilly
  • 43,296
  • 3
  • 59
  • 88
  • Excuse me for having an illustrative question about this proposed approach: would this rule-driven scheduling strategy solve the Apollo-Guidance-Computer system conflict during the Moon-landing phase or would rigid-scheduling concept longest-first result in un-avoidable Eagle crash to the Moon surface on handling the computer almost-overloading intensity of distance-measuring radar alarm signals? Would you mind to shed a light on what might have Mrs.Hamilton decided to solve in some other way in the AGC-scheduling so that she's saved the NASA mission in these sorts of process scheduling traps? – user3666197 Sep 03 '19 at 19:08
  • @user3666197 The problem of figuring out which low priority processes to kill so that critical functions can continue in a real-time system is a very different problem than this one. Also this is a greedy solution to an NP-complete solution. It will produce decent results for very little work. If it is not good enough, then you can look into more sophisticated algorithms that produce better answers but take more work. – btilly Sep 03 '19 at 20:50
  • Thanks for your kind remarks on subject. Given all the precedence rules are known beforehand, does a pi-calculus provide a rigorous solution without a need to resort to any NP-hard search at all, as Occam-pi language has provided in 80ies for Transputer-networks hardware, using just these explicitly known 1+ forest(s) of nested **`SEQ`** sections for known dependency trees and at least one, implicitly present, top-level, governing **`PAR`** section, as directives, where dependency chains were thus followed and pi-calculus warranted to become safely mapped onto the available hardware resources? – user3666197 Sep 03 '19 at 21:48
  • @user3666197 Trivially not. An NP-hard or NP-complete problem can't be transformed into one that is not by sprinkling fancy terminology around. (Unless you've found a way to earn yourself a million dollars by proving that P=NP.) In fact not only is this problem NP-hard, but the simplest case with no dependencies between jobs is *also* NP-hard. In fact that is the [bin-packing problem](https://en.wikipedia.org/wiki/Bin_packing_problem). – btilly Sep 03 '19 at 23:04
0

Example of Solution

This was used by ESA Giotto Satellite to fly into deep Space: the Halley Comet.

Pioneered by prof. C. A. R. Hoare a new, deterministic (unless external events/stimuli/ALTs present) scheduling was made possible for solving concurrency problems.

Based on a π-calculus driven scheduling, the π-algebra solves dependencies for N-jobs on M-resources given DAG dependencies and permits both variable job-duration and inter-process-communications ( using a pioneered technique of separating jobs, keeping them pair-interconnected with cheap, fast and exclusive-use CSP-channels, if needs exist, to exchange pieces of information in a still deterministic and scheduling non-devastating manner ).

 ::: occam-pi executed all the N-jobs as per dependencies were specified
 :::                   using pi-calculus algebra for deterministic
 :::                   scheduling N-jobs over a pool of M-resources available
 :::                   w.r.t. all dependencies
 ::: 
 job:   1      job duration:    15 timer:  545252301 START.... __
 job:  11      job duration:     9 timer:  545252302 START.... __
 job:  11      job duration:     9 timer:  545252317 FINISH... __
 job:  21      job duration:     8 timer:  545252447 START.... __
 job:  21      job duration:     8 timer:  545252489 FINISH... __
 job:  20      job duration:    16 timer:  545252447 START.... __
 job:  20      job duration:    16 timer:  545252538 FINISH... __
 job:  19      job duration:     7 timer:  545252447 START.... __
 job:  19      job duration:     7 timer:  545252614 FINISH... __
 job:  12      job duration:     9 timer:  545252447 START.... __
 job:  12      job duration:     9 timer:  545252659 FINISH... __
 job:  13      job duration:     8 timer:  545252682 START.... __
 job:  13      job duration:     8 timer:  545252704 FINISH... __
 job:  14      job duration:     7 timer:  545252727 START.... __
 job:  14      job duration:     7 timer:  545252752 FINISH... __
 job:  15      job duration:     6 timer:  545252775 START.... __
 job:  15      job duration:     6 timer:  545252798 FINISH... __
 job:  16      job duration:     5 timer:  545252819 START.... __
 job:  16      job duration:     5 timer:  545252840 FINISH... __
 job:  17      job duration:     4 timer:  545252862 START.... __
 job:  17      job duration:     4 timer:  545252886 FINISH... __
 job:  18      job duration:     9 timer:  545252908 START.... __
 job:  18      job duration:     9 timer:  545252930 FINISH... __
 job:   8      job duration:     2 timer:  545252301 START.... __
 job:   8      job duration:     2 timer:  545252976 FINISH... __
 job:   9      job duration:     5 timer:  545252999 START.... __
 job:   9      job duration:     5 timer:  545253022 FINISH... __
 job:  10      job duration:    31 timer:  545253044 START.... __
 job:  10      job duration:    31 timer:  545253093 FINISH... __
 job:   1      job duration:    15 timer:  545252416 FINISH... __
 job:   5      job duration:    31 timer:  545253142 START.... __
 job:   5      job duration:    31 timer:  545253230 FINISH... __
 job:   6      job duration:    27 timer:  545253242 START.... __
 job:   6      job duration:    27 timer:  545253309 FINISH... __
 job:   7      job duration:    11 timer:  545253324 START.... __
 job:   7      job duration:    11 timer:  545253347 FINISH... __
 job:   4      job duration:    12 timer:  545253141 START.... __
 job:   4      job duration:    12 timer:  545253392 FINISH... __
 job:   2      job duration:    24 timer:  545253141 START.... __
 job:   2      job duration:    24 timer:  545253436 FINISH... __
 job:   3      job duration:     6 timer:  545253460 START.... __
 job:   3      job duration:     6 timer:  545253482 FINISH... __

This main() used an exemplified case of DAG-defined dependencies for ~ 21-jobs having variable durations and was run on-line:

PROC main(CHAN BYTE keyboard, screen, error)
  
  [25]CHAN messagePROTOCOL mon :
  CHAN     messagePROTOCOL prn :
  
  PAR   -- DAG-root-node-(sorry for naive ASCII-art)-*-*-*-*-* DAG root-node's subtree(s)
    --                                               | | | | |
    SysPrintr(screen!, prn?)-------------------------+ | | | |
    --                                                 | | | |
    SysMonMUX(   prn!, mon?)---------------------------+ | | |
    --                                                   | | |
    SEQ -- ----------------------------------------------: | |
      --                                                 | | |
      job(      1, 15, mon[ 1]!)   --job_1---------------+ | |
      --                                                 | | |
      PAR ----------------------------------------+-+-+--* | |
        --                                        | | |    | |
        job(    4, 12, mon[ 4]!)   --     job_4---+ | |    | |
        --                                          | |    | |
        SEQ ----------------------------------------: |    | |
          --                                        | |    | |
          job(  2, 24, mon[ 2]!)   --     job_2-----+ |    | |
          job(  3,  6, mon[ 3]!)   --          _3---+ |    | |
        SEQ ------------------------------------------:    | |
          job(  5, 31, mon[ 5]!)   --     job_5-------+    | |
          job(  6, 27, mon[ 6]!)   --          _6-----+    | |
          job(  7, 11, mon[ 7]!)   --            _7---+    | |
    SEQ ---------------------------------------------------: |
      --                                                   | |
      job(      8,  2, mon[ 8]!)   --job_8-----------------+ |
      job(      9,  5, mon[ 9]!)   --     job_9------------+ |
      job(     10, 31, mon[10]!)   --          job_10------+ |
    SEQ -----------------------------------------------------:
      --                                                     |
      job(     11,  9, mon[11]!)   --job_11------------------+
      PAR ---------------------------------------------------*---*-*-*-*
        --                                                       | | | |
        job(   21,  8, mon[21]!)   --      job_21----------------+ | | |
        job(   20, 16, mon[20]!)   --      job_20----------------|-+ | |
        job(   19,  7, mon[19]!)   --      job_19----------------|-|-+ |
        SEQ -----------------------------------------------------------:
          --                                                           |
          job( 12,  9, mon[12]!)   --      job_12----------------------+
          job( 13,  8, mon[13]!)   --            _13-------------------+
          job( 14,  7, mon[14]!)   --               _14----------------+
          job( 15,  6, mon[15]!)   --                  _15-------------+
          job( 16,  5, mon[16]!)   --                     _16----------+
          job( 17,  4, mon[17]!)   --                        _17-------+
          job( 18,  9, mon[18]!)   --                           _18----+
: -- main() ------------------------------------------------------------

The code is made runnable online for any experimentation with true-[PARALLEL] system designs - the only pity is, that InMOS Transputers got substituted by their platform's x86-CPU-core(s) with restricted powers for "wilder" PAR-sections.

Enjoy any further hacking into π-calculus driven scheduling:

#INCLUDE "course.module"                  -- #USE "course.lib"

--                      +------+--------+----+----------+
--                      | jobID|duration|time|aSTRING[] |
--      +---------------+------+--------+----+----------+
--      |messagePROTOCOL|   INT;     INT; INT; [10]BYTE |
PROTOCOL messagePROTOCOL IS INT;     INT; INT; [10]BYTE : -- Error-occ21-.code.tio.occ(7)- array type must have dimension specified
--      +---------------+------+--------+----+----------+

PROC job ( VAL INT id, VAL INT duration, CHAN messagePROTOCOL outP )
  
  INT               t  :                   -- declare INT
  TIMER aCentralCLOCK  :                   -- declare TIMER
  [10]BYTE  flag.START :                   -- declare [10]BYTE
  [10]BYTE  flag.FINISH:                   -- declare [10]BYTE
   
  SEQ ------------------------------------------------------------------SEQ:
    flag.START  := " START...."                                         -- .SET
    flag.FINISH := " FINISH..."                                         -- .SET
                                                                        
    aCentralCLOCK ?      t                                              -- .read ? .SET t from timer at start
    outP ! id; duration; t; flag.START                                  -- .outP ! messagePROTOCOL{ id; duration; t; aString }
                                                                        
    aCentralCLOCK ? AFTER ( t PLUS duration )                           -- .read ? .wait  till ( start PLUS duration )
    --              ^^^^^                                               
    --              |||||                                               
    --              |||||                                               
    --              +++++------------------------------------------------- <this> emulates a variable <job>-duration
    
    aCentralCLOCK ?      t                                              -- .read / .SET t from timer at finish
    outP ! id; duration; t; flag.FINISH                                 -- .outP ! messagePROTOCOL{ id; duration; t; aString }
: -- job() -------------------------------------------------------------

PROC SysPrintr(CHAN BYTE outCH, CHAN messagePROTOCOL inCH )
  
  INT      iJobID   :
  INT      iDuration:
  INT      iTimer   :
  [10]BYTE aString  :
  
  WHILE TRUE
    SEQ
      --GET ? ----------------------------------------------------------
      inCH  ? iJobID; iDuration; iTimer; aString
      --PRINT ---------------------------outCH--------------------------
      out.string(   " job: ",         5, outCH )
      out.int(       iJobID,          3, outCH )
      
      out.string( " job duration: ", 20, outCH )
      out.int(         iDuration,     5, outCH )
      
      out.string( " timer: ",         8, outCH )
      out.int(     iTimer,           10, outCH )
      
      SEQ i = 0 FOR SIZE aString
        outCH ! aString[i]
      
      out.string(     "__*n",         4, outCH )
      flush(                             outCH )                        -- .flush
: -- SysPrintr() -------------------------------------------------------

PROC SysMonMUX(CHAN messagePROTOCOL out!, []CHAN messagePROTOCOL in?)
  INT      iJobID   :
  INT      iDuration:
  INT      iTimer   :
  [10]BYTE aString  :
  
  WHILE TRUE
    ALT
      in[ 0] ? iJobID; iDuration; iTimer; aString
        out  ! iJobID; iDuration; iTimer; aString
      in[ 1] ? iJobID; iDuration; iTimer; aString
        out  ! iJobID; iDuration; iTimer; aString
      in[ 2] ? iJobID; iDuration; iTimer; aString
        out  ! iJobID; iDuration; iTimer; aString
      in[ 3] ? iJobID; iDuration; iTimer; aString
        out  ! iJobID; iDuration; iTimer; aString
      in[ 4] ? iJobID; iDuration; iTimer; aString
        out  ! iJobID; iDuration; iTimer; aString
      in[ 5] ? iJobID; iDuration; iTimer; aString
        out  ! iJobID; iDuration; iTimer; aString
      in[ 6] ? iJobID; iDuration; iTimer; aString
        out  ! iJobID; iDuration; iTimer; aString
      in[ 7] ? iJobID; iDuration; iTimer; aString
        out  ! iJobID; iDuration; iTimer; aString
      in[ 8] ? iJobID; iDuration; iTimer; aString
        out  ! iJobID; iDuration; iTimer; aString
      in[ 9] ? iJobID; iDuration; iTimer; aString
        out  ! iJobID; iDuration; iTimer; aString
      in[10] ? iJobID; iDuration; iTimer; aString
        out  ! iJobID; iDuration; iTimer; aString
      in[11] ? iJobID; iDuration; iTimer; aString
        out  ! iJobID; iDuration; iTimer; aString
      in[12] ? iJobID; iDuration; iTimer; aString
        out  ! iJobID; iDuration; iTimer; aString
      in[13] ? iJobID; iDuration; iTimer; aString
        out  ! iJobID; iDuration; iTimer; aString
      in[14] ? iJobID; iDuration; iTimer; aString
        out  ! iJobID; iDuration; iTimer; aString
      in[15] ? iJobID; iDuration; iTimer; aString
        out  ! iJobID; iDuration; iTimer; aString
      in[16] ? iJobID; iDuration; iTimer; aString
        out  ! iJobID; iDuration; iTimer; aString
      in[17] ? iJobID; iDuration; iTimer; aString
        out  ! iJobID; iDuration; iTimer; aString
      in[18] ? iJobID; iDuration; iTimer; aString
        out  ! iJobID; iDuration; iTimer; aString
      in[19] ? iJobID; iDuration; iTimer; aString
        out  ! iJobID; iDuration; iTimer; aString
      in[20] ? iJobID; iDuration; iTimer; aString
        out  ! iJobID; iDuration; iTimer; aString
      in[21] ? iJobID; iDuration; iTimer; aString
        out  ! iJobID; iDuration; iTimer; aString
      in[22] ? iJobID; iDuration; iTimer; aString
        out  ! iJobID; iDuration; iTimer; aString
: -- SysMonMUX()--------------------------------------------------------

PROC main(CHAN BYTE keyboard, screen, error)
  
  [25]CHAN messagePROTOCOL mon :
  CHAN     messagePROTOCOL prn :
  
  PAR   -- DAG-root-node ----------------------------*-*-*-*-* DAG root-node's subtree(s)
    --                                               | | | | |
    SysPrintr(screen!, prn?)-------------------------+ | | | |
    --                                                 | | | |
    SysMonMUX(   prn!, mon?)---------------------------+ | | |
    --                                                   | | |
    SEQ -- ----------------------------------------------: | |
      --                                                 | | |
      job(      1, 15, mon[ 1]!)   --job_1---------------+ | |
      --                                                 | | |
      PAR ----------------------------------------+-+-+--* | |
        --                                        | | |    | |
        job(    4, 12, mon[ 4]!)   --     job_4---+ | |    | |
        --                                          | |    | |
        SEQ ----------------------------------------: |    | |
          --                                        | |    | |
          job(  2, 24, mon[ 2]!)   --     job_2-----+ |    | |
          job(  3,  6, mon[ 3]!)   --          _3---+ |    | |
        SEQ ------------------------------------------:    | |
          job(  5, 31, mon[ 5]!)   --     job_5-------+    | |
          job(  6, 27, mon[ 6]!)   --          _6-----+    | |
          job(  7, 11, mon[ 7]!)   --            _7---+    | |
    SEQ ---------------------------------------------------* |
      --                                                   | |
      job(      8,  2, mon[ 8]!)   --job_8-----------------+ |
      job(      9,  5, mon[ 9]!)   --     job_9------------+ |
      job(     10, 31, mon[10]!)   --          job_10------+ |
    SEQ -----------------------------------------------------*
      --                                                     |
      job(     11,  9, mon[11]!)   --job_11------------------+
      PAR ---------------------------------------------------*---*-*-*-*
        --                                                       | | | |
        job(   21,  8, mon[21]!)   --      job_21----------------+ | | |
        job(   20, 16, mon[20]!)   --      job_20----------------|-+ | |
        job(   19,  7, mon[19]!)   --      job_19----------------|-|-+ |
        SEQ -----------------------------------------------------------+
          --                                                           |
          job( 12,  9, mon[12]!)   --      job_12----------------------+
          job( 13,  8, mon[13]!)   --            _13-------------------+
          job( 14,  7, mon[14]!)   --               _14----------------+
          job( 15,  6, mon[15]!)   --                  _15-------------+
          job( 16,  5, mon[16]!)   --                     _16----------+
          job( 17,  4, mon[17]!)   --                        _17-------+
          job( 18,  9, mon[18]!)   --                           _18----+
: -- main() ------------------------------------------------------------

Credit : credit goes to @bazza for reminding of this memorable Occam-pi space mission.

halfer
  • 19,824
  • 17
  • 99
  • 186
user3666197
  • 1
  • 6
  • 50
  • 92
  • This will provide a working solution with all requirements satisfied. I believe that it is not guaranteed to produce an optimal one. – btilly Sep 05 '19 at 18:17
  • @btilly Let me ask you for a kind explicit formulation of the Criterial Function (Utility function),that you have considered(yet not mentioned,so far)for a rigorous measuring,benchmarking & comparing any two different solutions (so as to finally arrive to an optimal one) Ref. the cybernetics / theory of systems & extremal control thereof to drive a system so as to follow a given Utility Function to reach a max (Utility) or to reach a min, in case of using an inversely formulated Criterial Function a Penalty Function. Having a different arbitrary functions can&will move the "optimum", won't it? – user3666197 Sep 05 '19 at 23:47
  • The question is to schedule `n` jobs on `m` machines such that the wall clock time from start of the first to the finish of the last is as short as possible. That is an NP-hard problem, and the simpler question of whether it is possible to do it in time `x` is NP-complete. – btilly Sep 06 '19 at 00:11
  • @btilly I do understand your view.The O/P,nevertheless,has not stated "your" Criterial Function,but set a set of different rules(exactly speaking a pair of such ones)yet,as it is very common,the Business has another insight of what are all the "costs",that ought be put into the(Business-owner formulated)Criterial Function (not all academically formulated problems identify or even try recognise to have an impact into the overall Utility/Penalty of sought for solution candidates).Thank you for your kind cooperation on clarifying points&views on this topic&appreciate your kind response on this – user3666197 Sep 06 '19 at 00:22