|
|
|
@ -212,9 +212,12 @@ func (s *Scheduler) processPending(ctx context.Context) { |
|
|
|
} else if loadedCount == 0 { |
|
|
|
// No models loaded. Load the model but prefer the best fit.
|
|
|
|
slog.Debug("loading first model", "model", pending.model.ModelPath) |
|
|
|
g := pickBestFitGPUs(pending, ggml, gpus, &numParallel) |
|
|
|
g := pickBestFullFitByLibrary(pending, ggml, gpus, &numParallel) |
|
|
|
if g != nil { |
|
|
|
gpus = g |
|
|
|
} else { |
|
|
|
// Only allow partial loads when this is the first model
|
|
|
|
gpus = pickBestPartialFitByLibrary(pending, ggml, gpus, &numParallel) |
|
|
|
} |
|
|
|
s.loadFn(pending, ggml, gpus, numParallel) |
|
|
|
break |
|
|
|
@ -231,7 +234,7 @@ func (s *Scheduler) processPending(ctx context.Context) { |
|
|
|
|
|
|
|
// Update free memory from currently loaded models
|
|
|
|
s.updateFreeSpace(availGpus) |
|
|
|
fitGpus := pickBestFitGPUs(pending, ggml, availGpus, &numParallel) |
|
|
|
fitGpus := pickBestFullFitByLibrary(pending, ggml, availGpus, &numParallel) |
|
|
|
if fitGpus != nil { |
|
|
|
slog.Debug("new model fits with existing models, loading") |
|
|
|
s.loadFn(pending, ggml, fitGpus, numParallel) |
|
|
|
@ -668,11 +671,12 @@ func (a ByDuration) Less(i, j int) bool { |
|
|
|
// func (a BySize) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
|
|
|
|
// func (a BySize) Less(i, j int) bool { return a[i].estimatedVRAM < a[j].estimatedVRAM }
|
|
|
|
|
|
|
|
// pickBestFitGPUs will try to find the optimal placement of the model in the available GPUs where the model fully fits
|
|
|
|
// pickBestFullFitByLibrary will try to find the optimal placement of the model in the available GPUs where the model fully fits
|
|
|
|
// The list of GPUs returned will always be the same brand (library)
|
|
|
|
// If the model can not be fit fully within the available GPU(s) nil is returned
|
|
|
|
// If numParallel is <= 0, this will attempt try to optimize parallism based on available VRAM, and adjust
|
|
|
|
// opts.NumCtx accordingly
|
|
|
|
func pickBestFitGPUs(req *LlmRequest, ggml *llm.GGML, gpus gpu.GpuInfoList, numParallel *int) gpu.GpuInfoList { |
|
|
|
func pickBestFullFitByLibrary(req *LlmRequest, ggml *llm.GGML, gpus gpu.GpuInfoList, numParallel *int) gpu.GpuInfoList { |
|
|
|
var estimatedVRAM uint64 |
|
|
|
|
|
|
|
var numParallelToTry []int |
|
|
|
@ -723,6 +727,25 @@ func pickBestFitGPUs(req *LlmRequest, ggml *llm.GGML, gpus gpu.GpuInfoList, numP |
|
|
|
return nil |
|
|
|
} |
|
|
|
|
|
|
|
// If multiple Libraries are detected, pick the Library which loads the most layers for the model
|
|
|
|
func pickBestPartialFitByLibrary(req *LlmRequest, ggml *llm.GGML, gpus gpu.GpuInfoList, numParallel *int) gpu.GpuInfoList { |
|
|
|
*numParallel = 1 |
|
|
|
byLibrary := gpus.ByLibrary() |
|
|
|
if len(byLibrary) <= 1 { |
|
|
|
return gpus |
|
|
|
} |
|
|
|
var bestEstimate uint64 |
|
|
|
var bestFit int |
|
|
|
for i, gl := range byLibrary { |
|
|
|
_, estimatedVRAM := llm.PredictServerFit(gl, ggml, req.model.AdapterPaths, req.model.ProjectorPaths, req.opts) |
|
|
|
if estimatedVRAM > bestEstimate { |
|
|
|
bestEstimate = estimatedVRAM |
|
|
|
bestFit = i |
|
|
|
} |
|
|
|
} |
|
|
|
return byLibrary[bestFit] |
|
|
|
} |
|
|
|
|
|
|
|
// findRunnerToUnload finds a runner to unload to make room for a new model
|
|
|
|
func (s *Scheduler) findRunnerToUnload() *runnerRef { |
|
|
|
s.loadedMu.Lock() |
|
|
|
|