1
0
mirror of https://github.com/newnius/YAO-scheduler.git synced 2025-06-08 14:51:55 +00:00
YAO-scheduler/src/ga.go

266 lines
6.8 KiB
Go
Raw Normal View History

2020-05-14 12:52:39 +00:00
package main
import (
"math/rand"
"github.com/MaxHalford/eaopt"
2020-05-26 12:46:11 +00:00
"math"
2020-05-25 03:35:44 +00:00
log "github.com/sirupsen/logrus"
2020-05-14 12:52:39 +00:00
)
2020-05-25 03:35:44 +00:00
// A resource allocation
2020-05-14 12:52:39 +00:00
type Allocation struct {
TasksOnNode map[string][]Task // tasks on nodes[id]
2020-05-25 03:35:44 +00:00
Nodes map[string]NodeStatus
2020-05-18 00:52:33 +00:00
NodeIDs []string
Flags map[string]bool
Evaluator Evaluator
2020-05-26 12:46:11 +00:00
Tasks []Task
2020-05-14 12:52:39 +00:00
}
2020-05-18 00:52:33 +00:00
/* Evaluate the allocation */
func (X Allocation) Evaluate() (float64, error) {
if !X.Flags["valid"] {
//fmt.Println("Invalid allocation")
return math.MaxFloat64, nil
2020-05-14 12:52:39 +00:00
}
2020-05-18 00:52:33 +00:00
2020-05-26 12:46:11 +00:00
var nodes []NodeStatus
for _, node := range X.Nodes {
nodes = append(nodes, node)
}
eva := Evaluator{}
eva.init(nodes, X.Tasks)
for node, tasks := range X.TasksOnNode {
for _, task := range tasks {
eva.add(X.Nodes[node], task)
}
}
cost := eva.calculate()
//log.Info(cost)
2020-05-27 10:04:05 +00:00
//return float64(cost) + float64(len(X.Nodes)), nil
return float64(cost) + float64(len(X.Nodes))/float64(len(X.Tasks)), nil
//return float64(cost), nil
2020-05-18 00:52:33 +00:00
}
2020-05-27 10:04:05 +00:00
// Mutate a Vector by resampling each element from a normal distribution with probability 0.8.
2020-05-18 00:52:33 +00:00
func (X Allocation) Mutate(rng *rand.Rand) {
/* remove a node randomly */
2020-05-27 10:04:05 +00:00
// make sure n > 0 && round >0
round := rng.Intn(1+len(X.Nodes)/100)%50 + 1
for i := 0; i < round; i++ {
2020-05-18 00:52:33 +00:00
if !X.Flags["valid"] {
return
}
//fmt.Println("Mutate")
//fmt.Println("Before", X)
var nodeIDs []string
for nodeID := range X.Nodes {
nodeIDs = append(nodeIDs, nodeID)
}
randIndex := rng.Intn(len(X.Nodes))
nodeID := nodeIDs[randIndex]
/* reschedule tasks on tgt node */
var tasks []Task
if _, ok := X.TasksOnNode[nodeID]; ok {
for _, task := range X.TasksOnNode[nodeID] {
tasks = append(tasks, task)
}
delete(X.TasksOnNode, nodeID)
}
2020-05-27 10:04:05 +00:00
//log.Info("Delete node ", nodeID)
//log.Info("Before ", X.Nodes)
2020-05-18 00:52:33 +00:00
delete(X.Nodes, nodeID)
2020-05-27 10:04:05 +00:00
//log.Info("After ", X.Nodes)
2020-05-18 00:52:33 +00:00
//fmt.Println(tasks)
2020-05-27 11:12:17 +00:00
/* random-fit */
2020-05-14 12:52:39 +00:00
for _, task := range tasks {
2020-05-27 11:12:17 +00:00
if nodeID, ok := randomFit(X, task); ok {
2020-05-18 00:52:33 +00:00
X.TasksOnNode[nodeID] = append(X.TasksOnNode[nodeID], task)
2020-05-27 10:04:05 +00:00
cnt := task.NumberGPU
//log.Info("Add task ", task.Name, " in ", nodeID)
//log.Info("Before ", X.Nodes[nodeID].Status)
2020-05-26 12:46:11 +00:00
for i := range X.Nodes[nodeID].Status {
if X.Nodes[nodeID].Status[i].MemoryAllocated == 0 {
X.Nodes[nodeID].Status[i].MemoryAllocated += task.MemoryGPU
2020-05-27 10:04:05 +00:00
cnt--
if cnt == 0 {
break
}
2020-05-26 12:46:11 +00:00
}
}
2020-05-27 10:04:05 +00:00
//log.Info("After ", X.Nodes[nodeID].Status)
2020-05-18 00:52:33 +00:00
} else {
X.Flags["valid"] = false
2020-05-27 10:04:05 +00:00
break
2020-05-18 00:52:33 +00:00
}
2020-05-14 12:52:39 +00:00
}
}
2020-05-26 12:46:11 +00:00
return
2020-05-23 13:06:31 +00:00
/* move tasks */
2020-05-18 00:52:33 +00:00
if !X.Flags["valid"] {
//fmt.Println("Invalid allocation")
return
}
2020-05-14 12:52:39 +00:00
var nodeIDs []string
2020-05-18 00:52:33 +00:00
for nodeID := range X.Nodes {
2020-05-14 12:52:39 +00:00
nodeIDs = append(nodeIDs, nodeID)
}
2020-05-18 00:52:33 +00:00
randIndex1 := rng.Intn(len(nodeIDs))
nodeID1 := nodeIDs[randIndex1]
2020-05-23 13:06:31 +00:00
if tasks, ok := X.TasksOnNode[nodeID1]; ok && len(tasks) > 0 {
idx := rng.Intn(len(tasks))
task := tasks[idx]
copy(X.TasksOnNode[nodeID1][idx:], X.TasksOnNode[nodeID1][idx+1:])
X.TasksOnNode[nodeID1] = X.TasksOnNode[nodeID1][:len(X.TasksOnNode[nodeID1])-1]
if nodeID, ok := firstFit(X, task); ok {
X.TasksOnNode[nodeID] = append(X.TasksOnNode[nodeID], task)
} else {
X.Flags["valid"] = false
}
}
2020-05-18 00:52:33 +00:00
}
2020-05-14 12:52:39 +00:00
2020-05-18 00:52:33 +00:00
// Crossover a Vector with another Vector by applying uniform crossover.
func (X Allocation) Crossover(Y eaopt.Genome, rng *rand.Rand) {
2020-05-27 10:04:05 +00:00
// make sure n > 0 && round > 0
round := rng.Intn(1+len(X.Nodes)/100)%10 + 1
for i := 0; i < round; i++ {
2020-05-18 00:52:33 +00:00
if !Y.(Allocation).Flags["valid"] || !X.Flags["valid"] {
return
2020-05-14 12:52:39 +00:00
}
2020-05-18 00:52:33 +00:00
taskToNode := map[string]string{}
for nodeID, tasks := range X.TasksOnNode {
for _, task := range tasks {
taskToNode[task.Name] = nodeID
2020-05-14 12:52:39 +00:00
}
}
2020-05-18 00:52:33 +00:00
var nodeIDs []string
for nodeID := range Y.(Allocation).Nodes {
nodeIDs = append(nodeIDs, nodeID)
2020-05-14 12:52:39 +00:00
}
2020-05-18 00:52:33 +00:00
//fmt.Println(nodeIDs, Y.(Allocation))
randIndex := rng.Intn(len(nodeIDs))
nodeID := nodeIDs[randIndex]
2020-05-27 10:04:05 +00:00
/* remove duplicated tasks */
2020-05-18 00:52:33 +00:00
for _, task := range Y.(Allocation).TasksOnNode[nodeID] {
//fmt.Println(Y.(Allocation).TasksOnNode[nodeID])
idx := -1
nodeID2, ok := taskToNode[task.Name]
if !ok {
2020-05-27 10:04:05 +00:00
log.Warn("Error", taskToNode, X.TasksOnNode, task.Name)
2020-05-18 00:52:33 +00:00
}
for i, task2 := range X.TasksOnNode[nodeID2] {
if task2.Name == task.Name {
idx = i
}
}
if idx == -1 {
2020-05-27 10:04:05 +00:00
log.Warn("Error 2", taskToNode, X.TasksOnNode, task.Name)
2020-05-18 00:52:33 +00:00
}
//fmt.Println(X.TasksOnNode)
copy(X.TasksOnNode[nodeID2][idx:], X.TasksOnNode[nodeID2][idx+1:])
X.TasksOnNode[nodeID2] = X.TasksOnNode[nodeID2][:len(X.TasksOnNode[nodeID2])-1]
2020-05-27 10:04:05 +00:00
cnt := task.NumberGPU
//log.Info("Remove task ", task.Name, " in ", nodeID2)
//log.Info("Before ", X.Nodes[nodeID2].Status)
for i := range X.Nodes[nodeID2].Status {
/* TODO: determine correct GPU */
if X.Nodes[nodeID2].Status[i].MemoryAllocated == task.MemoryGPU {
X.Nodes[nodeID2].Status[i].MemoryAllocated -= task.MemoryGPU
cnt--
if cnt == 0 {
break
}
2020-05-26 12:46:11 +00:00
}
}
2020-05-27 10:04:05 +00:00
if cnt != 0 {
log.Warn("Cross remove ", cnt)
}
//log.Info("After ", X.Nodes[nodeID].Status)
2020-05-18 00:52:33 +00:00
//fmt.Println(X.TasksOnNode)
}
2020-05-27 10:04:05 +00:00
2020-05-18 00:52:33 +00:00
/* reschedule tasks on tgt node */
var tasks []Task
if _, ok := X.TasksOnNode[nodeID]; ok {
for _, task := range X.TasksOnNode[nodeID] {
tasks = append(tasks, task)
}
delete(X.TasksOnNode, nodeID)
2020-05-14 12:52:39 +00:00
}
2020-05-18 00:52:33 +00:00
if _, ok := X.Nodes[nodeID]; ok {
delete(X.Nodes, nodeID)
}
2020-05-27 10:04:05 +00:00
X.Nodes[nodeID] = Y.(Allocation).Nodes[nodeID].Copy()
2020-05-14 12:52:39 +00:00
2020-05-18 00:52:33 +00:00
var newTasksOnNode []Task
for _, task := range Y.(Allocation).TasksOnNode[nodeID] {
newTasksOnNode = append(newTasksOnNode, task)
}
X.TasksOnNode[nodeID] = newTasksOnNode
2020-05-14 12:52:39 +00:00
2020-05-27 11:12:17 +00:00
/* random-fit */
2020-05-18 00:52:33 +00:00
for _, task := range tasks {
2020-05-27 11:12:17 +00:00
if nodeID, ok := randomFit(X, task); ok {
2020-05-18 00:52:33 +00:00
X.TasksOnNode[nodeID] = append(X.TasksOnNode[nodeID], task)
2020-05-27 10:04:05 +00:00
cnt := task.NumberGPU
//log.Info("Remove task ", task.Name, " in ", nodeID)
//log.Info("Before ", X.Nodes[nodeID].Status)
2020-05-26 12:46:11 +00:00
for i := range X.Nodes[nodeID].Status {
if X.Nodes[nodeID].Status[i].MemoryAllocated == 0 {
X.Nodes[nodeID].Status[i].MemoryAllocated += task.MemoryGPU
2020-05-27 10:04:05 +00:00
cnt--
if cnt == 0 {
break
}
2020-05-26 12:46:11 +00:00
}
}
2020-05-27 10:04:05 +00:00
//log.Info("After ", X.Nodes[nodeID].Status)
if cnt != 0 {
log.Warn("cross add", cnt)
}
2020-05-18 00:52:33 +00:00
} else {
X.Flags["valid"] = false
2020-05-27 10:04:05 +00:00
break
2020-05-14 12:52:39 +00:00
}
}
}
//fmt.Println()
//fmt.Println("crossover", X.TasksOnNode)
}
// Clone a Vector to produce a new one that points to a different slice.
func (X Allocation) Clone() eaopt.Genome {
2020-05-18 00:52:33 +00:00
if !X.Flags["valid"] {
2020-05-14 12:52:39 +00:00
//fmt.Println(X.Valid)
}
2020-05-25 03:35:44 +00:00
Y := Allocation{TasksOnNode: map[string][]Task{}, Nodes: map[string]NodeStatus{}, Flags: map[string]bool{"valid": X.Flags["valid"]}}
2020-05-14 12:52:39 +00:00
for id, node := range X.Nodes {
2020-05-27 10:04:05 +00:00
Y.Nodes[id] = node.Copy()
2020-05-18 00:52:33 +00:00
Y.NodeIDs = append(Y.NodeIDs, node.ClientID)
2020-05-14 12:52:39 +00:00
}
for id, tasks := range X.TasksOnNode {
var t []Task
for _, task := range tasks {
t = append(t, task)
}
Y.TasksOnNode[id] = t
}
2020-05-27 10:04:05 +00:00
Y.Tasks = X.Tasks
2020-05-14 12:52:39 +00:00
return Y
}