CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutSign UpSign In
sagemathinc

Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.

GitHub Repository: sagemathinc/cocalc
Path: blob/master/src/packages/frontend/compute/cloud/hyperstack/gpu.tsx
Views: 687
1
/*
2
NOT USED RIGHT NOW -- It's in compeition with machine-types.tsx
3
but that made more progress and is perhaps better overall, with
4
a filter, than this.
5
*/
6
7
import { Select } from "antd";
8
import { getModelLinks } from "./util";
9
import { useMemo } from "react";
10
import { A } from "@cocalc/frontend/components/A";
11
import { Icon } from "@cocalc/frontend/components/icon";
12
import { r_join } from "@cocalc/frontend/components/r_join";
13
import { filterOption } from "@cocalc/frontend/compute/util";
14
import { markup } from "@cocalc/util/compute/cloud/hyperstack/pricing";
15
import {
16
DEFAULT_FLAVOR,
17
DEFAULT_REGION,
18
} from "@cocalc/util/compute/cloud/hyperstack/api-types";
19
import {
20
bestCount,
21
getModelOptions,
22
getCountOptions,
23
parseFlavor,
24
encodeFlavor,
25
} from "./flavor";
26
import { capitalize, currency, plural } from "@cocalc/util/misc";
27
import { GPU_SPECS } from "@cocalc/util/compute/gpu-specs";
28
import { toGPU } from "./util";
29
30
export default function GPU({
31
priceData,
32
setConfig,
33
configuration,
34
disabled,
35
state,
36
}) {
37
const region_name = configuration.region_name ?? DEFAULT_REGION;
38
const flavor_name = configuration.flavor_name ?? DEFAULT_FLAVOR;
39
40
// Links is cosmetic to give an overview for users of what range of GPU models
41
// are available.
42
const links = useMemo(
43
() => (priceData == null ? null : getModelLinks(priceData)),
44
[priceData],
45
);
46
47
const modelOptions = useMemo(() => {
48
if (priceData == null) {
49
return null;
50
}
51
return getModelOptions(priceData).map(
52
({ region, model, available, cost_per_hour, gpu }) => {
53
const disabled =
54
(state != "deprovisioned" && region != region_name) || available == 0;
55
const i = model.indexOf("-");
56
const display = model.slice(i + 1);
57
const gpuSpec = GPU_SPECS[toGPU(gpu)];
58
return {
59
disabled,
60
label: (
61
<div style={{ display: "flex" }}>
62
<div style={{ flex: 1.25 }}>NVIDIA {display}</div>
63
<div style={{ flex: 1.25 }}>
64
~{currency(markup({ cost: cost_per_hour, priceData }))}/hour per
65
GPU
66
</div>
67
<div style={{ flex: 1 }}>
68
{gpuSpec != null && <>GPU RAM: {gpuSpec.memory} GB</>}
69
</div>
70
<div style={{ flex: 0.75 }}>
71
{capitalize(region.toLowerCase().split("-")[0])} šŸƒ
72
</div>
73
</div>
74
),
75
value: `${region}|${model}`,
76
search: `${display} ${region.toLowerCase()} ram:${gpuSpec?.memory}`,
77
};
78
},
79
);
80
}, [priceData, configuration.region_name]);
81
82
const countOptions = useMemo(() => {
83
if (priceData == null) {
84
return null;
85
}
86
return getCountOptions({
87
flavor_name: configuration.flavor_name,
88
priceData,
89
region_name,
90
}).map(({ count, available = 0, cost_per_hour, gpu, quantity }) => {
91
const gpuSpec = GPU_SPECS[toGPU(gpu)];
92
return {
93
value: count,
94
label: (
95
<div style={{ display: "flex" }}>
96
<div style={{ flex: 1 }}>Ɨ {count} </div>
97
<div style={{ flex: 1 }}>
98
{currency(markup({ cost: cost_per_hour, priceData }))}/hour
99
</div>
100
<div style={{ flex: 1 }}>
101
{gpuSpec?.memory != null && (
102
<>GPU RAM: {quantity * gpuSpec.memory} GB</>
103
)}
104
</div>
105
<div style={{ flex: 1 }}>
106
{available} {plural(available, "GPU")} available
107
</div>
108
</div>
109
),
110
search: `${count} available:${available} ram:${
111
quantity * (gpuSpec?.memory ?? 0)
112
}`,
113
disabled: !available,
114
};
115
});
116
}, [priceData, configuration.region_name, configuration.flavor_name]);
117
118
if (priceData == null || links == null || modelOptions == null) {
119
return null;
120
}
121
122
const head = (
123
<div style={{ color: "#666", marginBottom: "5px" }}>
124
<b>
125
<Icon name="cube" /> NVIDIA GPU:{" "}
126
{r_join(
127
links.map(({ name, url }) => {
128
return url ? (
129
<A key={name} href={url}>
130
{name}
131
</A>
132
) : (
133
<span key={name}>{name}</span>
134
);
135
}),
136
)}
137
</b>
138
<br />
139
{state == "running"
140
? "You can only change the GPU model or quantity when the compute server is off or deprovisioned."
141
: "Configure your server by selecting your GPU and quantity here, or select a machine type below."}
142
{state == "off"
143
? " You can only change the region if your compute server is deprovisioned."
144
: ""}
145
<div style={{ marginTop: "10px" }}>
146
<Select
147
disabled={disabled}
148
style={{ width: "100%" }}
149
options={modelOptions as any}
150
value={`${region_name}|${parseFlavor(flavor_name).model}`}
151
onChange={(value) => {
152
const [region, model] = value.split("|");
153
setConfig({
154
region_name: region,
155
flavor_name: encodeFlavor({
156
model,
157
count: bestCount({
158
model,
159
region,
160
count: parseFlavor(flavor_name).count,
161
priceData,
162
}),
163
}),
164
});
165
}}
166
showSearch
167
optionFilterProp="children"
168
filterOption={filterOption}
169
/>
170
<div style={{ display: "flex", marginTop: "10px" }}>
171
<div
172
style={{
173
marginRight: "30px",
174
display: "flex",
175
alignItems: "center",
176
fontSize: "11pt",
177
}}
178
>
179
Number of GPUs
180
</div>
181
<div style={{ flex: 1 }}>
182
<Select
183
disabled={disabled}
184
style={{ width: "100%" }}
185
options={countOptions as any}
186
value={parseFlavor(flavor_name).count}
187
onChange={(count) => {
188
setConfig({
189
flavor_name: encodeFlavor({
190
...parseFlavor(flavor_name),
191
count,
192
}),
193
});
194
}}
195
showSearch
196
optionFilterProp="children"
197
filterOption={filterOption}
198
/>
199
</div>
200
</div>
201
</div>
202
</div>
203
);
204
return head;
205
}
206
207
208
209