242 lines
7.2 KiB
Go
242 lines
7.2 KiB
Go
package algo
|
||
|
||
import (
|
||
"errors"
|
||
"gonum.org/v1/gonum/mat"
|
||
"math"
|
||
)
|
||
|
||
type k8sStrategy struct {
|
||
ProviderList []*Provider
|
||
Task *Task
|
||
StrategyList []*Strategy
|
||
}
|
||
|
||
func NewK8sStrategy(task *Task, providers ...*Provider) *k8sStrategy {
|
||
var providerList []*Provider
|
||
var res [][]int
|
||
|
||
for _, p := range providers {
|
||
p.GenMaxResourceNum(task)
|
||
providerList = append(providerList, p)
|
||
}
|
||
|
||
back_trace_task(task.Replicas, 0, providerList, 0, &res, 0)
|
||
|
||
var strategyList []*Strategy
|
||
for _, r := range res {
|
||
var path []int
|
||
var pathlist [][]int
|
||
|
||
var resourcePerProvider []int
|
||
for j, p := range providerList {
|
||
if r[j] > p.MaxReplicas {
|
||
resourcePerProvider = append(resourcePerProvider, p.MaxReplicas)
|
||
} else {
|
||
resourcePerProvider = append(resourcePerProvider, r[j])
|
||
}
|
||
}
|
||
back_trace_resource(resourcePerProvider, 0, path, &pathlist)
|
||
strategy := NewStrategy()
|
||
strategy.Tasksolution = r
|
||
strategy.Resourcesolution = pathlist
|
||
strategyList = append(strategyList, strategy)
|
||
}
|
||
|
||
return &k8sStrategy{ProviderList: providerList, Task: task, StrategyList: strategyList}
|
||
}
|
||
|
||
func (ps *k8sStrategy) computeMaxScore() (*Task, error) {
|
||
maxStrategy := NewStrategy()
|
||
var maxprofit float64
|
||
|
||
//先计算出最大的利润值
|
||
for _, strategy := range ps.StrategyList {
|
||
for _, resourceSolu := range strategy.Resourcesolution {
|
||
profit := computeProfit(ps.Task, strategy.Tasksolution, resourceSolu, ps.ProviderList)
|
||
if profit > maxprofit {
|
||
maxprofit = profit
|
||
}
|
||
}
|
||
}
|
||
|
||
for _, strategy := range ps.StrategyList {
|
||
for _, resourceSolu := range strategy.Resourcesolution {
|
||
profit := computeProfit(ps.Task, strategy.Tasksolution, resourceSolu, ps.ProviderList)
|
||
highDegree := computeHighDegree(ps.Task, resourceSolu, ps.ProviderList)
|
||
|
||
valueSum := profit/maxprofit + highDegree
|
||
|
||
//将每个确定任务分配策略的最高的策略得分存储到里面
|
||
if valueSum > maxStrategy.ValueSum {
|
||
strategy.Profit = profit
|
||
strategy.HighDegree = highDegree
|
||
}
|
||
|
||
if valueSum > maxStrategy.ValueSum {
|
||
maxStrategy.ValueSum = valueSum
|
||
maxStrategy.Tasksolution = strategy.Tasksolution
|
||
newResourceSolu := [][]int{}
|
||
newResourceSolu = append(newResourceSolu, resourceSolu)
|
||
maxStrategy.Resourcesolution = newResourceSolu
|
||
maxStrategy.Profit = profit
|
||
maxStrategy.HighDegree = highDegree
|
||
}
|
||
}
|
||
}
|
||
|
||
if len(ps.ProviderList) == 0 {
|
||
return nil, errors.New("empty providers")
|
||
}
|
||
|
||
ps.Task.MaxscoreStrategy = maxStrategy // 记录该任务的最终分配策略
|
||
return ps.Task, nil
|
||
}
|
||
|
||
func computeProfit(task *Task, tasksolution []int, resourcesolution []int, providerList []*Provider) float64 {
|
||
var timeexecution int //记录任务的实际最大执行时间
|
||
var costSum float64 //该任务在多个云厂商所需支付的成本总价
|
||
|
||
for i, provider := range providerList {
|
||
|
||
//如果该厂商分的任务为0,则直接跳过该厂商,循环到下一厂商
|
||
if tasksolution[i] == 0 {
|
||
continue
|
||
}
|
||
|
||
//先计算下该云厂商的执行时间ddl,并替换任务的最大执行时间,向上取整
|
||
t := math.Ceil(float64(tasksolution[i])/float64(resourcesolution[i])) * float64(task.Time)
|
||
if int(t) > timeexecution {
|
||
timeexecution = int(t)
|
||
}
|
||
|
||
//计算前几份资源多执行任务
|
||
forOneMoreTaskNUm := tasksolution[i] % resourcesolution[i]
|
||
|
||
for j := 0; j < resourcesolution[i]; j++ {
|
||
if j < forOneMoreTaskNUm {
|
||
t = math.Ceil(float64(tasksolution[i])/float64(resourcesolution[i])) * float64(task.Time)
|
||
} else {
|
||
t = math.Floor(float64(tasksolution[i])/float64(resourcesolution[i])) * float64(task.Time)
|
||
}
|
||
|
||
//如果这份资源分的的任务数
|
||
cost := (provider.CpuCost*task.Cpu + provider.MemCost*task.Mem + provider.DiskCost*task.Disk) * t * (math.Pow(float64(j+1), math.Log2(provider.LearnIndex)))
|
||
costSum += cost
|
||
}
|
||
|
||
}
|
||
|
||
//计算用户的支付价格pay
|
||
pay := task.Pr
|
||
if timeexecution == task.Time { //没有排队等待,且只有一个副本直接执行或者多个副本完全并行执行
|
||
if pay < costSum {
|
||
pay = costSum
|
||
}
|
||
} else if timeexecution >= task.T0 && timeexecution <= task.T1 { //有排队时间或者任务存在串行执行
|
||
if task.T1 == task.T0 { //仅有一个副本,时间中有排队时间
|
||
e := math.Exp(float64(-task.B) * float64(timeexecution-task.T1))
|
||
pay = (1 - 1/(1+e)) * task.Pr
|
||
} else { //多个副本
|
||
e := math.Exp(float64(-task.B) * float64(timeexecution-task.T1) / float64(task.T1-task.T0))
|
||
pay = (1 - 1/(1+e)) * task.Pr
|
||
}
|
||
|
||
if pay < costSum {
|
||
pay = costSum
|
||
}
|
||
} else { //超出用户满意度的完全串行时间
|
||
pay = 1 / 2 * task.Pr
|
||
if pay < costSum {
|
||
pay = costSum
|
||
}
|
||
}
|
||
|
||
profitSum := pay - costSum
|
||
return profitSum
|
||
}
|
||
|
||
func computeHighDegree(task *Task, resourcesolution []int, providerList []*Provider) float64 {
|
||
var highDegreeSum float64
|
||
// 依次计算每个云厂商的资源可用度
|
||
for i, provider := range providerList {
|
||
// 定义两个四维向量
|
||
// 未来任务资源需求比例
|
||
futureDemand := mat.NewVecDense(3, []float64{1, 1, 1})
|
||
|
||
// 定义假设按此方案分配后的剩余资源可用量,时间虽然有差异,但是先按那个时刻算吧,这里可能还要改一下
|
||
nowLeft_cpu := provider.CpuAvail - task.Cpu*float64(resourcesolution[i])
|
||
nowLeft_mem := provider.MemAvail - task.Mem*float64(resourcesolution[i])
|
||
nowLeft_disk := provider.DiskAvail - task.Disk*float64(resourcesolution[i])
|
||
|
||
nowLeft := mat.NewVecDense(3, []float64{nowLeft_cpu, nowLeft_mem, nowLeft_disk})
|
||
// 使用余弦相似度计算两个比值的相近度
|
||
// 计算向量的内积
|
||
dot_product := mat.Dot(futureDemand, nowLeft)
|
||
|
||
// 计算向量的模长
|
||
magnitude1 := mat.Norm(futureDemand, 2)
|
||
magnitude2 := mat.Norm(nowLeft, 2)
|
||
|
||
// 计算余弦相似度
|
||
cosine_similarity := dot_product / (magnitude1 * magnitude2)
|
||
highDegreeSum += cosine_similarity
|
||
}
|
||
|
||
return highDegreeSum / float64(len(providerList))
|
||
}
|
||
|
||
func back_trace_task(ReplicaNum int, DoneReplicasNum int, providerList []*Provider, staclu int, res *[][]int, sum int) {
|
||
//var count int = 0
|
||
pnum := len(providerList)
|
||
|
||
//所有的任务数都已经进行分配
|
||
if DoneReplicasNum == ReplicaNum {
|
||
var a []int
|
||
for i := 0; i < pnum; i++ {
|
||
a = append(a, providerList[i].CurReplicas)
|
||
}
|
||
*res = append(*res, a)
|
||
//(*res)[0] = append((*res)[0], a)
|
||
//count += 1
|
||
return
|
||
}
|
||
|
||
//遍历完所有的云厂商序号
|
||
if staclu >= pnum {
|
||
return
|
||
}
|
||
|
||
if providerList[staclu].CurReplicas < providerList[staclu].MaxTaskCanRun {
|
||
providerList[staclu].CurReplicas += 1
|
||
back_trace_task(ReplicaNum, DoneReplicasNum+1, providerList, staclu, res, sum)
|
||
providerList[staclu].CurReplicas -= 1
|
||
back_trace_task(ReplicaNum, DoneReplicasNum, providerList, staclu+1, res, sum)
|
||
} else {
|
||
back_trace_task(ReplicaNum, DoneReplicasNum, providerList, staclu+1, res, sum)
|
||
}
|
||
|
||
}
|
||
|
||
func back_trace_resource(list []int, i int, path []int, pathlist *[][]int) {
|
||
if i == len(list) {
|
||
var pathCopy = make([]int, len(path))
|
||
copy(pathCopy, path)
|
||
*pathlist = append(*pathlist, pathCopy)
|
||
return
|
||
}
|
||
|
||
if list[i] == 0 {
|
||
path = append(path, 0)
|
||
back_trace_resource(list, i+1, path, pathlist)
|
||
path = path[:len(path)-1]
|
||
} else {
|
||
for j := 1; j < list[i]+1; j++ {
|
||
path = append(path, j)
|
||
back_trace_resource(list, i+1, path, pathlist)
|
||
path = path[:len(path)-1]
|
||
}
|
||
}
|
||
|
||
}
|