2020-05-25 21:41:39 +08:00
|
|
|
package main
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"strconv"
|
|
|
|
|
"math/rand"
|
|
|
|
|
"time"
|
2020-05-26 20:46:11 +08:00
|
|
|
log "github.com/sirupsen/logrus"
|
2020-05-25 21:41:39 +08:00
|
|
|
"github.com/MaxHalford/eaopt"
|
|
|
|
|
"math"
|
|
|
|
|
"testing"
|
|
|
|
|
)
|
|
|
|
|
|
2020-05-26 20:46:11 +08:00
|
|
|
func TgenerateCase() ([]NodeStatus, []Task) {
|
|
|
|
|
numTask := 6
|
2020-05-25 21:41:39 +08:00
|
|
|
|
2020-05-26 20:46:11 +08:00
|
|
|
var nodes []NodeStatus
|
|
|
|
|
var tasks []Task
|
2020-05-25 21:41:39 +08:00
|
|
|
|
|
|
|
|
for i := 0; i < numTask*3; i++ {
|
2020-05-26 20:46:11 +08:00
|
|
|
node := NodeStatus{ClientID: strconv.Itoa(i), Rack: "Rack-" + strconv.Itoa(i%40), Domain: "Domain-" + strconv.Itoa(i%4)}
|
2020-05-25 21:41:39 +08:00
|
|
|
node.NumCPU = 24
|
2020-05-26 20:46:11 +08:00
|
|
|
node.UtilCPU = 2.0
|
2020-05-25 21:41:39 +08:00
|
|
|
node.MemTotal = 188
|
2020-05-26 20:46:11 +08:00
|
|
|
node.MemAvailable = 20
|
2020-05-25 21:41:39 +08:00
|
|
|
node.TotalBW = 100
|
2020-05-26 20:46:11 +08:00
|
|
|
//cnt := 4
|
2020-05-25 21:41:39 +08:00
|
|
|
cnt := rand.Intn(3) + 1
|
|
|
|
|
for i := 0; i < cnt; i++ {
|
2020-05-26 20:46:11 +08:00
|
|
|
node.Status = append(node.Status, GPUStatus{MemoryTotal: 11439, MemoryAllocated: 0, UUID: node.ClientID + "-" + strconv.Itoa(i)})
|
2020-05-25 21:41:39 +08:00
|
|
|
}
|
2020-05-26 20:46:11 +08:00
|
|
|
nodes = append(nodes, node)
|
2020-05-25 21:41:39 +08:00
|
|
|
}
|
|
|
|
|
for i := 0; i < numTask; i++ {
|
|
|
|
|
isPS := false
|
2020-05-26 20:46:11 +08:00
|
|
|
if i%4 == 0 {
|
2020-05-25 21:41:39 +08:00
|
|
|
isPS = true
|
|
|
|
|
}
|
2020-05-26 20:46:11 +08:00
|
|
|
task := Task{Name: "task-" + strconv.Itoa(i), IsPS: isPS}
|
2020-05-25 21:41:39 +08:00
|
|
|
task.Memory = 4
|
|
|
|
|
task.NumberCPU = 2
|
|
|
|
|
task.NumberGPU = 1
|
2020-05-26 20:46:11 +08:00
|
|
|
task.MemoryGPU = 4096
|
|
|
|
|
tasks = append(tasks, task)
|
2020-05-25 21:41:39 +08:00
|
|
|
}
|
2020-05-26 20:46:11 +08:00
|
|
|
return nodes, tasks
|
|
|
|
|
}
|
2020-05-25 21:41:39 +08:00
|
|
|
|
2020-05-26 20:46:11 +08:00
|
|
|
func TestBestFit(t *testing.T) {
|
|
|
|
|
nodes, tasks := TgenerateCase()
|
|
|
|
|
for _, node := range nodes {
|
|
|
|
|
log.Info(node)
|
2020-05-25 21:41:39 +08:00
|
|
|
}
|
|
|
|
|
s := time.Now()
|
|
|
|
|
allocation := fastBestFit(nodes, tasks)
|
|
|
|
|
log.Println(time.Since(s))
|
2020-05-26 20:46:11 +08:00
|
|
|
log.Println(allocation)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestGA(t *testing.T) {
|
|
|
|
|
return
|
|
|
|
|
nodes, tasks := TgenerateCase()
|
2020-05-25 21:41:39 +08:00
|
|
|
|
|
|
|
|
// Instantiate a GA with a GAConfig
|
|
|
|
|
var ga, err = eaopt.NewDefaultGAConfig().NewGA()
|
|
|
|
|
if err != nil {
|
|
|
|
|
log.Println(err)
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Set the number of generations to run for
|
|
|
|
|
ga.NGenerations = math.MaxInt32
|
|
|
|
|
ga.NPops = 1
|
2020-05-26 20:46:11 +08:00
|
|
|
ga.PopSize = 30 + uint(len(tasks)/2)
|
2020-05-25 21:41:39 +08:00
|
|
|
|
|
|
|
|
// Add a custom print function to track progress
|
|
|
|
|
ga.Callback = func(ga *eaopt.GA) {
|
|
|
|
|
log.Printf("Best fitness at generation %d: %f\n", ga.Generations, ga.HallOfFame[0].Fitness)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bestFitness := math.MaxFloat64
|
|
|
|
|
count := 0
|
|
|
|
|
|
|
|
|
|
ts := time.Now()
|
|
|
|
|
|
|
|
|
|
ga.EarlyStop = func(ga *eaopt.GA) bool {
|
|
|
|
|
gap := math.Abs(ga.HallOfFame[0].Fitness - bestFitness)
|
|
|
|
|
if gap <= 0.000001 || ga.HallOfFame[0].Fitness >= bestFitness {
|
|
|
|
|
if count >= 30 || time.Since(ts) > time.Second*30 {
|
|
|
|
|
log.Println("Early Stop")
|
|
|
|
|
return true
|
|
|
|
|
} else {
|
|
|
|
|
count++
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
bestFitness = ga.HallOfFame[0].Fitness
|
|
|
|
|
count = 1
|
|
|
|
|
}
|
|
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
|
2020-05-26 20:46:11 +08:00
|
|
|
var f = func(rng *rand.Rand) eaopt.Genome {
|
|
|
|
|
allocation := Allocation{TasksOnNode: map[string][]Task{}, Nodes: map[string]NodeStatus{}, Flags: map[string]bool{"valid": true}}
|
|
|
|
|
|
|
|
|
|
//log.Println(nodes)
|
|
|
|
|
var nodesT []NodeStatus
|
|
|
|
|
for _, node := range nodes {
|
|
|
|
|
nodesT = append(nodesT, node.Copy())
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//nodesT[0].Status[0].MemoryAllocated = 100
|
|
|
|
|
//log.Println(nodes[0].Status[0].MemoryAllocated)
|
|
|
|
|
|
|
|
|
|
//log.Println(&nodesT[0])
|
|
|
|
|
//log.Println(&nodes[0])
|
|
|
|
|
|
|
|
|
|
for _, node := range nodesT {
|
|
|
|
|
allocation.Nodes[node.ClientID] = node
|
|
|
|
|
}
|
|
|
|
|
for _, task := range tasks {
|
|
|
|
|
allocation.Tasks = append(allocation.Tasks, task)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* shuffle */
|
|
|
|
|
for n := len(tasks); n > 0; n-- {
|
|
|
|
|
randIndex := rng.Intn(n)
|
|
|
|
|
allocation.Tasks[n-1], allocation.Tasks[randIndex] = allocation.Tasks[randIndex], allocation.Tasks[n-1]
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* pick nodes */
|
|
|
|
|
for _, node := range nodesT {
|
|
|
|
|
allocation.Nodes[node.ClientID] = node
|
|
|
|
|
allocation.NodeIDs = append(allocation.NodeIDs, node.ClientID)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
t := rng.Int() % 10
|
|
|
|
|
if t == 0 {
|
|
|
|
|
/* best-fit */
|
|
|
|
|
ts := time.Now()
|
|
|
|
|
allocation.TasksOnNode = fastBestFit(nodesT, tasks).TasksOnNode
|
|
|
|
|
log.Println(time.Since(ts))
|
|
|
|
|
//fmt.Println("Best Fit")
|
|
|
|
|
} else if t%2 == 0 {
|
|
|
|
|
/* first-fit */
|
|
|
|
|
for _, task := range tasks {
|
|
|
|
|
if nodeID, ok := randomFit(allocation, task); ok {
|
|
|
|
|
allocation.TasksOnNode[nodeID] = append(allocation.TasksOnNode[nodeID], task)
|
|
|
|
|
for i := range allocation.Nodes[nodeID].Status {
|
|
|
|
|
if allocation.Nodes[nodeID].Status[i].MemoryAllocated == 0 {
|
|
|
|
|
allocation.Nodes[nodeID].Status[i].MemoryAllocated += task.MemoryGPU
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
allocation.Flags["valid"] = false
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
/* random-fit */
|
|
|
|
|
for _, task := range tasks {
|
|
|
|
|
if nodeID, ok := randomFit(allocation, task); ok {
|
|
|
|
|
allocation.TasksOnNode[nodeID] = append(allocation.TasksOnNode[nodeID], task)
|
|
|
|
|
for i := range allocation.Nodes[nodeID].Status {
|
|
|
|
|
if allocation.Nodes[nodeID].Status[i].MemoryAllocated == 0 {
|
|
|
|
|
allocation.Nodes[nodeID].Status[i].MemoryAllocated += task.MemoryGPU
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
allocation.Flags["valid"] = false
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
//fmt.Println(evaluatue(allocation))
|
|
|
|
|
//fmt.Println(allocation)
|
|
|
|
|
return allocation
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
2020-05-25 21:41:39 +08:00
|
|
|
// Find the minimum
|
2020-05-26 20:46:11 +08:00
|
|
|
err = ga.Minimize(f)
|
2020-05-25 21:41:39 +08:00
|
|
|
log.Println(time.Since(ts))
|
|
|
|
|
log.Println(ga.HallOfFame[0].Genome.(Allocation).TasksOnNode)
|
2020-05-26 20:46:11 +08:00
|
|
|
//log.Println(ga.HallOfFame[0].Genome.(Allocation).Flags)
|
|
|
|
|
//log.Println(ga.HallOfFame[0].Genome.(Allocation).Nodes)
|
2020-05-25 21:41:39 +08:00
|
|
|
if err != nil {
|
|
|
|
|
log.Println(err)
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
}
|