aboutsummaryrefslogtreecommitdiffstats
path: root/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/autoscale/Load.java
blob: 22c13795d184569f7d4aa8c09735f0e318de6efb (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
// Copyright Vespa.ai. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.vespa.hosted.provision.autoscale;

import com.yahoo.config.provision.NodeResources;

import java.util.function.DoubleBinaryOperator;
import java.util.function.DoubleUnaryOperator;
import java.util.function.Predicate;

/**
 * The load of a node or system, measured as fractions of max (1.0) in three dimensions.
 *
 * @author bratseth
 */
public record Load(double cpu, double memory, double disk, double gpu, double gpuMemory) {

    public enum Dimension { cpu, memory, disk, gpu, gpuMemory }

    public Load(double cpu, double memory, double disk, double gpu, double gpuMemory) {
        this.cpu = requireNormalized(cpu, "cpu");
        this.memory = requireNormalized(memory, "memory");
        this.disk = requireNormalized(disk, "disk");
        this.gpu = requireNormalized(gpu, "gpu");
        this.gpuMemory = requireNormalized(gpuMemory, "gpuMemory");
    }

    public double cpu() { return cpu; }
    public double memory() { return memory; }
    public double disk() { return disk; }
    public double gpu() { return gpu; }
    public double gpuMemory() { return gpuMemory; }

    public Load withCpu(double cpu) { return new Load(cpu, memory, disk, gpu, gpuMemory); }
    public Load withMemory(double memory) { return new Load(cpu, memory, disk, gpu, gpuMemory); }
    public Load withDisk(double disk) { return new Load(cpu, memory, disk, gpu, gpuMemory); }
    public Load withGpu(double gpu) { return new Load(cpu, memory, disk, gpu, gpuMemory); }
    public Load withGpuMemory(double gpuMemory) { return new Load(cpu, memory, disk, gpu, gpuMemory); }

    public Load add(Load other) {
        return join(other, (a, b) -> a + b);
    }

    public Load multiply(NodeResources resources) {
        return new Load(cpu * resources.vcpu(), memory * resources.memoryGb(), disk * resources.diskGb(), gpu * resources.gpuResources().count(), gpu * resources.gpuResources().memoryGb());
    }
    public Load multiply(double factor) {
        return map(v -> v * factor);
    }
    public Load multiply(Load other) {
        return join(other, (a, b) -> a * b);
    }

    public Load divide(Load divisor) {
        return join(divisor, (a, b) -> divide(a, b));
    }
    public Load divide(double divisor) {
        return map(v -> divide(v, divisor));
    }
    public Load divide(NodeResources resources) {
        return new Load(divide(cpu, resources.vcpu()), divide(memory, resources.memoryGb()), divide(disk, resources.diskGb()), divide(gpu, resources.gpuResources().count()), divide(gpuMemory, resources.gpuResources().memoryGb()));
    }

    /** Returns the load where the given function is applied to each dimension of this. */
    public Load map(DoubleUnaryOperator f) {
        return new Load(f.applyAsDouble(cpu),
                        f.applyAsDouble(memory),
                        f.applyAsDouble(disk),
                        f.applyAsDouble(gpu),
                        f.applyAsDouble(gpuMemory));
    }

    /** Returns the load where the given function is applied to each dimension of this and the given load. */
    public Load join(Load other, DoubleBinaryOperator f) {
        return new Load(f.applyAsDouble(this.cpu(), other.cpu()),
                        f.applyAsDouble(this.memory(), other.memory()),
                        f.applyAsDouble(this.disk(), other.disk()),
                        f.applyAsDouble(this.gpu(), other.gpu()),
                        f.applyAsDouble(this.gpuMemory(), other.gpuMemory()));
    }

    /** Returns true if any dimension matches the predicate. */
    public boolean any(Predicate<Double> test) {
        return test.test(cpu) || test.test(memory) || test.test(disk);
    }

    public NodeResources scaled(NodeResources resources) {
        return resources.withVcpu(cpu * resources.vcpu())
                        .withMemoryGb(memory * resources.memoryGb())
                        .withDiskGb(disk * resources.diskGb());
    }

    public double get(Dimension dimension) {
        return switch (dimension) {
            case cpu -> cpu();
            case memory -> memory();
            case disk -> disk();
            case gpu -> gpu();
            case gpuMemory -> gpuMemory();
        };
    }

    private double requireNormalized(double value, String name) {
        if (Double.isNaN(value))
            throw new IllegalArgumentException(name + " must be a number but is NaN");
        if (value < 0)
            throw new IllegalArgumentException(name + " must be zero or larger, but is " + value);
        return value;
    }

    private static double divide(double a, double b) {
        if (a == 0 && b == 0) return 0;
        return a / b;
    }

    @Override
    public String toString() {
        return "load: " + cpu + " cpu, " + memory + " memory, " + disk + " disk," + gpu + " gpu," + gpuMemory + " gpuMemory";
    }

    public static Load zero() { return new Load(0, 0, 0, 0, 0); }
    public static Load one() { return new Load(1, 1, 1, 1, 1); }

    public static Load byDividing(NodeResources a, NodeResources b) {
        return new Load(divide(a.vcpu(), b.vcpu()),
                        divide(a.memoryGb(), b.memoryGb()),
                        divide(a.diskGb(), b.diskGb()),
                        divide(a.gpuResources().count(), b.gpuResources().count()),
                        divide(a.gpuResources().memoryGb(), b.gpuResources().memoryGb()));
    }

}