2019-10-31 14:32:46 -04:00
// Copyright (C) 2018 spdfg
2018-10-06 20:03:14 -07:00
//
// This file is part of Elektron.
//
// Elektron is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Elektron is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Elektron. If not, see <http://www.gnu.org/licenses/>.
//
2018-01-31 19:00:31 -05:00
package schedUtils
import (
2018-09-30 18:23:38 -07:00
"log"
2019-10-31 14:32:46 -04:00
"github.com/spdfg/elektron/def"
"github.com/spdfg/elektron/utilities"
2018-01-31 19:00:31 -05:00
)
// Criteria for resizing the scheduling window.
type SchedulingWindowResizingCriteria string
2018-02-02 17:06:59 -05:00
var SchedWindowResizingCritToStrategy = map [ SchedulingWindowResizingCriteria ] SchedWindowResizingStrategy {
2018-01-31 19:00:31 -05:00
"fillNextOfferCycle" : & fillNextOfferCycle { } ,
}
// Interface for a scheduling window resizing strategy.
2018-02-02 17:06:59 -05:00
type SchedWindowResizingStrategy interface {
2018-02-16 21:49:12 +00:00
// Apply the window resizing strategy and return the size of the scheduling window and the number tasks that
// were traversed in the process.
// The size of the scheduling window would correspond to the total number of
// instances (flattened) that can be scheduled in the next offer cycle.
// The number of tasks would correspond to number of different tasks (instances not included).
Apply ( func ( ) interface { } ) ( int , int )
2018-01-31 19:00:31 -05:00
}
// Scheduling window resizing strategy that attempts to resize the scheduling window
// to include as many tasks as possible so as to make the most use of the next offer cycle.
2018-02-02 17:07:23 -05:00
type fillNextOfferCycle struct { }
2018-01-31 19:00:31 -05:00
2018-02-16 21:49:12 +00:00
func ( s * fillNextOfferCycle ) Apply ( getArgs func ( ) interface { } ) ( int , int ) {
2018-01-31 19:00:31 -05:00
return s . apply ( getArgs ( ) . ( [ ] def . Task ) )
}
// Loop over the unscheduled tasks, in submission order, and determine the maximum
// number of tasks that can be scheduled in the next offer cycle.
// As the offers get smaller and smaller, this approach might lead to an increase in internal fragmentation.
//
// Note: To be able to make the most use of the next offer cycle, one would need to perform a non-polynomial search
// which is computationally expensive.
2018-02-16 21:49:12 +00:00
func ( s * fillNextOfferCycle ) apply ( taskQueue [ ] def . Task ) ( int , int ) {
2018-01-31 19:00:31 -05:00
clusterwideResourceCount := utilities . GetClusterwideResourceAvailability ( )
newSchedWindow := 0
filledCPU := 0.0
filledRAM := 0.0
// Can we schedule another task.
canSchedule := func ( t def . Task ) bool {
if ( ( filledCPU + t . CPU ) <= clusterwideResourceCount . UnusedCPU ) &&
( ( filledRAM + t . RAM ) <= clusterwideResourceCount . UnusedRAM ) {
return true
}
return false
}
2018-02-02 17:06:59 -05:00
done := false
2018-02-16 21:49:12 +00:00
// Track of number of tasks traversed.
numberOfTasksTraversed := 0
2018-01-31 19:00:31 -05:00
for _ , task := range taskQueue {
2018-02-16 21:49:12 +00:00
numberOfTasksTraversed ++
2018-01-31 19:00:31 -05:00
for i := * task . Instances ; i > 0 ; i -- {
2018-02-02 17:06:59 -05:00
log . Printf ( "Checking if Instance #%d of Task[%s] can be scheduled " +
"during the next offer cycle..." , i , task . Name )
2018-01-31 19:00:31 -05:00
if canSchedule ( task ) {
filledCPU += task . CPU
filledRAM += task . RAM
newSchedWindow ++
} else {
2018-02-02 17:06:59 -05:00
done = true
2018-02-16 21:49:12 +00:00
if i == * task . Instances {
// We don't count this task if none of the instances could be scheduled.
numberOfTasksTraversed --
}
2018-01-31 19:00:31 -05:00
break
}
}
2018-02-02 17:06:59 -05:00
if done {
break
}
2018-01-31 19:00:31 -05:00
}
2018-04-17 20:09:35 +00:00
// Hacking...
// 2^window is window<=7
// if newSchedWindow <= 7 {
// newSchedWindow = int(math.Pow(2.0, float64(newSchedWindow)))
// }
// Another hack. Getting rid of window to see whether the idle power consumption can be amortized.
// Setting window as the length of the entire queue.
// Also setting numberOfTasksTraversed to the number of tasks in the entire queue.
// TODO: Create another resizing strategy that sizes the window to the length of the entire pending queue.
2018-04-17 20:12:33 +00:00
// flattenedLength := 0
// numTasks := 0
// for _, ts := range taskQueue {
// numTasks++
// flattenedLength += *ts.Instances
// }
// newSchedWindow = flattenedLength
// numberOfTasksTraversed = numTasks
2018-04-17 20:09:35 +00:00
2018-02-16 21:49:12 +00:00
return newSchedWindow , numberOfTasksTraversed
2018-01-31 19:00:31 -05:00
}