github.com/projecteru2/core@v0.0.0-20240321043226-06bcc1c23f58/resource/plugins/cpumem/calculate.go (about) 1 package cpumem 2 3 import ( 4 "context" 5 6 "github.com/cockroachdb/errors" 7 "github.com/mitchellh/mapstructure" 8 "github.com/projecteru2/core/log" 9 "github.com/projecteru2/core/resource/plugins/cpumem/schedule" 10 cpumemtypes "github.com/projecteru2/core/resource/plugins/cpumem/types" 11 plugintypes "github.com/projecteru2/core/resource/plugins/types" 12 coretypes "github.com/projecteru2/core/types" 13 ) 14 15 // CalculateDeploy . 16 func (p Plugin) CalculateDeploy(ctx context.Context, nodename string, deployCount int, resourceRequest plugintypes.WorkloadResourceRequest) (*plugintypes.CalculateDeployResponse, error) { 17 logger := log.WithFunc("resource.cpumem.CalculateDeploy").WithField("node", nodename) 18 req := &cpumemtypes.WorkloadResourceRequest{} 19 if err := req.Parse(resourceRequest); err != nil { 20 return nil, err 21 } 22 if err := req.Validate(); err != nil { 23 logger.Errorf(ctx, err, "invalid resource opts %+v", req) 24 return nil, err 25 } 26 27 nodeResourceInfo, err := p.doGetNodeResourceInfo(ctx, nodename) 28 if err != nil { 29 logger.WithField("node", nodename).Error(ctx, err) 30 return nil, err 31 } 32 33 var enginesParams []*cpumemtypes.EngineParams 34 var workloadsResource []*cpumemtypes.WorkloadResource 35 36 if !req.CPUBind { 37 enginesParams, workloadsResource, err = p.doAllocByMemory(nodeResourceInfo, deployCount, req) 38 } else { 39 enginesParams, workloadsResource, err = p.doAllocByCPU(nodeResourceInfo, deployCount, req) 40 } 41 if err != nil { 42 return nil, err 43 } 44 45 resp := &plugintypes.CalculateDeployResponse{} 46 return resp, mapstructure.Decode(map[string]any{ 47 "engines_params": enginesParams, 48 "workloads_resource": workloadsResource, 49 }, resp) 50 } 51 52 // CalculateRealloc . 53 func (p Plugin) CalculateRealloc(ctx context.Context, nodename string, resource plugintypes.WorkloadResource, resourceRequest plugintypes.WorkloadResourceRequest) (*plugintypes.CalculateReallocResponse, error) { 54 req := &cpumemtypes.WorkloadResourceRequest{} 55 if err := req.Parse(resourceRequest); err != nil { 56 return nil, err 57 } 58 originResource := &cpumemtypes.WorkloadResource{} 59 if err := originResource.Parse(resource); err != nil { 60 return nil, err 61 } 62 63 if req.KeepCPUBind { 64 req.CPUBind = len(originResource.CPUMap) > 0 65 } 66 67 nodeResourceInfo, err := p.doGetNodeResourceInfo(ctx, nodename) 68 if err != nil { 69 log.WithFunc("resource.cpumem.CalculateRealloc").WithField("node", nodename).Error(ctx, err, "failed to get resource info of node") 70 return nil, err 71 } 72 73 // put resources back into the resource pool 74 nodeResourceInfo.Usage.Sub(&cpumemtypes.NodeResource{ 75 CPU: originResource.CPURequest, 76 CPUMap: originResource.CPUMap, 77 Memory: originResource.MemoryRequest, 78 NUMAMemory: originResource.NUMAMemory, 79 }) 80 81 newReq := &cpumemtypes.WorkloadResourceRequest{ 82 CPUBind: req.CPUBind, 83 CPURequest: req.CPURequest + originResource.CPURequest, 84 CPULimit: req.CPULimit + originResource.CPULimit, 85 MemRequest: req.MemRequest + originResource.MemoryRequest, 86 MemLimit: req.MemLimit + originResource.MemoryLimit, 87 } 88 89 if err = newReq.Validate(); err != nil { 90 return nil, err 91 } 92 93 // if cpu was specified before, try to ensure cpu affinity 94 var cpuMap cpumemtypes.CPUMap 95 var numaNodeID string 96 var numaMemory cpumemtypes.NUMAMemory 97 98 if req.CPUBind { 99 cpuPlans := schedule.GetCPUPlans(nodeResourceInfo, originResource.CPUMap, p.config.Scheduler.ShareBase, p.config.Scheduler.MaxShare, newReq) 100 if len(cpuPlans) == 0 { 101 return nil, coretypes.ErrInsufficientResource 102 } 103 104 cpuPlan := cpuPlans[0] 105 cpuMap = cpuPlan.CPUMap 106 numaNodeID = cpuPlan.NUMANode 107 if len(numaNodeID) > 0 { 108 numaMemory = cpumemtypes.NUMAMemory{numaNodeID: newReq.MemRequest} 109 } 110 } else if _, _, err = p.doAllocByMemory(nodeResourceInfo, 1, newReq); err != nil { 111 return nil, err 112 } 113 114 engineParams := &cpumemtypes.EngineParams{ 115 CPU: newReq.CPULimit, 116 CPUMap: cpuMap, 117 NUMANode: numaNodeID, 118 Memory: newReq.MemLimit, 119 } 120 121 newResource := &cpumemtypes.WorkloadResource{ 122 CPURequest: newReq.CPURequest, 123 CPULimit: newReq.CPULimit, 124 MemoryRequest: newReq.MemRequest, 125 MemoryLimit: newReq.MemLimit, 126 CPUMap: cpuMap, 127 NUMAMemory: numaMemory, 128 NUMANode: numaNodeID, 129 } 130 131 deltaWorkloadResource := newResource.DeepCopy() 132 deltaWorkloadResource.Sub(originResource) 133 134 resp := &plugintypes.CalculateReallocResponse{} 135 return resp, mapstructure.Decode(map[string]any{ 136 "engine_params": engineParams, 137 "delta_resource": deltaWorkloadResource, 138 "workload_resource": newResource, 139 }, resp) 140 } 141 142 // CalculateRemap . 143 func (p Plugin) CalculateRemap(ctx context.Context, nodename string, workloadsResource map[string]plugintypes.WorkloadResource) (*plugintypes.CalculateRemapResponse, error) { 144 resp := &plugintypes.CalculateRemapResponse{} 145 engineParamsMap := map[string]*cpumemtypes.EngineParams{} 146 if len(workloadsResource) == 0 { 147 return resp, mapstructure.Decode(map[string]any{ 148 "engine_params_map": engineParamsMap, 149 }, resp) 150 } 151 152 workloadResourceMap := map[string]*cpumemtypes.WorkloadResource{} 153 for ID, workload := range workloadsResource { 154 workloadResource := &cpumemtypes.WorkloadResource{} 155 if err := workloadResource.Parse(workload); err != nil { 156 return nil, err 157 } 158 workloadResourceMap[ID] = workloadResource 159 } 160 161 nodeResourceInfo, err := p.doGetNodeResourceInfo(ctx, nodename) 162 if err != nil { 163 log.WithFunc("resource.cpumem.CalculateRemap").WithField("node", nodename).Error(ctx, err) 164 return nil, err 165 } 166 167 availableNodeResource := nodeResourceInfo.GetAvailableResource() 168 169 shareCPUMap := cpumemtypes.CPUMap{} 170 for cpu, pieces := range availableNodeResource.CPUMap { 171 if pieces >= p.config.Scheduler.ShareBase { 172 shareCPUMap[cpu] = p.config.Scheduler.ShareBase 173 } 174 } 175 176 if len(shareCPUMap) == 0 { 177 for cpu := range nodeResourceInfo.Capacity.CPUMap { 178 shareCPUMap[cpu] = p.config.Scheduler.ShareBase 179 } 180 } 181 182 for ID, workloadResource := range workloadResourceMap { 183 // only process workloads without cpu binding 184 if len(workloadResource.CPUMap) == 0 { 185 engineParamsMap[ID] = &cpumemtypes.EngineParams{ 186 CPU: workloadResource.CPULimit, 187 CPUMap: shareCPUMap, 188 NUMANode: workloadResource.NUMANode, 189 Memory: workloadResource.MemoryLimit, 190 Remap: true, 191 } 192 } 193 } 194 195 return resp, mapstructure.Decode(map[string]any{ 196 "engine_params_map": engineParamsMap, 197 }, resp) 198 } 199 200 func (p Plugin) doAllocByMemory(resourceInfo *cpumemtypes.NodeResourceInfo, deployCount int, req *cpumemtypes.WorkloadResourceRequest) ([]*cpumemtypes.EngineParams, []*cpumemtypes.WorkloadResource, error) { 201 if req.CPURequest > float64(len(resourceInfo.Capacity.CPUMap)) { 202 return nil, nil, errors.Wrap(coretypes.ErrInsufficientCapacity, "cpu") 203 } 204 205 availableResource := resourceInfo.GetAvailableResource() 206 if req.MemRequest > 0 && availableResource.Memory/req.MemRequest < int64(deployCount) { 207 return nil, nil, errors.Wrap(coretypes.ErrInsufficientCapacity, "memory") 208 } 209 210 enginesParams := []*cpumemtypes.EngineParams{} 211 workloadsResource := []*cpumemtypes.WorkloadResource{} 212 213 engineParams := &cpumemtypes.EngineParams{ 214 CPU: req.CPULimit, 215 Memory: req.MemLimit, 216 } 217 workloadResource := &cpumemtypes.WorkloadResource{ 218 CPURequest: req.CPURequest, 219 CPULimit: req.CPULimit, 220 MemoryRequest: req.MemRequest, 221 MemoryLimit: req.MemLimit, 222 } 223 224 for len(enginesParams) < deployCount { 225 enginesParams = append(enginesParams, engineParams) 226 workloadsResource = append(workloadsResource, workloadResource) 227 } 228 return enginesParams, workloadsResource, nil 229 } 230 231 func (p Plugin) doAllocByCPU(resourceInfo *cpumemtypes.NodeResourceInfo, deployCount int, req *cpumemtypes.WorkloadResourceRequest) ([]*cpumemtypes.EngineParams, []*cpumemtypes.WorkloadResource, error) { 232 cpuPlans := schedule.GetCPUPlans(resourceInfo, nil, p.config.Scheduler.ShareBase, p.config.Scheduler.MaxShare, req) 233 if len(cpuPlans) < deployCount { 234 return nil, nil, errors.Wrap(coretypes.ErrInsufficientCapacity, "cpu") 235 } 236 237 cpuPlans = cpuPlans[:deployCount] 238 enginesParams := []*cpumemtypes.EngineParams{} 239 workloadsResource := []*cpumemtypes.WorkloadResource{} 240 241 for _, cpuPlan := range cpuPlans { 242 enginesParams = append(enginesParams, &cpumemtypes.EngineParams{ 243 CPU: req.CPULimit, 244 CPUMap: cpuPlan.CPUMap, 245 NUMANode: cpuPlan.NUMANode, 246 Memory: req.MemLimit, 247 }) 248 249 workloadResource := &cpumemtypes.WorkloadResource{ 250 CPURequest: req.CPURequest, 251 CPULimit: req.CPULimit, 252 MemoryRequest: req.MemRequest, 253 MemoryLimit: req.MemLimit, 254 CPUMap: cpuPlan.CPUMap, 255 NUMANode: cpuPlan.NUMANode, 256 } 257 if len(workloadResource.NUMANode) > 0 { 258 workloadResource.NUMAMemory = cpumemtypes.NUMAMemory{workloadResource.NUMANode: workloadResource.MemoryRequest} 259 } 260 261 workloadsResource = append(workloadsResource, workloadResource) 262 } 263 264 return enginesParams, workloadsResource, nil 265 }