Update app.py
Browse files
app.py
CHANGED
@@ -3,37 +3,56 @@ import plotly.graph_objects as go
|
|
3 |
import pandas as pd
|
4 |
import numpy as np
|
5 |
|
6 |
-
def
|
7 |
requests_per_hour,
|
8 |
execution_time_per_request,
|
9 |
active_cost_per_second,
|
10 |
flex_cost_per_second,
|
11 |
-
cold_start_penalty
|
12 |
-
|
|
|
13 |
):
|
14 |
-
#
|
15 |
-
|
|
|
|
|
16 |
|
17 |
-
#
|
18 |
-
|
19 |
-
|
20 |
|
21 |
-
#
|
22 |
-
|
23 |
|
24 |
-
#
|
25 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
26 |
|
27 |
-
# Total monthly cost
|
28 |
total_monthly_cost = active_cost + flex_cost
|
29 |
|
30 |
return {
|
31 |
"monthly_requests": monthly_requests,
|
32 |
-
"active_requests":
|
33 |
"flex_requests": flex_requests,
|
34 |
"active_cost": active_cost,
|
35 |
"flex_cost": flex_cost,
|
36 |
-
"total_monthly_cost": total_monthly_cost
|
|
|
37 |
}
|
38 |
|
39 |
def generate_cost_projection(
|
@@ -42,28 +61,31 @@ def generate_cost_projection(
|
|
42 |
execution_time,
|
43 |
active_cost,
|
44 |
flex_cost,
|
45 |
-
|
46 |
-
cold_start
|
|
|
47 |
):
|
48 |
# Generate data for different request volumes
|
49 |
request_volumes = np.linspace(min_requests, max_requests, 20)
|
50 |
results = []
|
51 |
|
52 |
for req_vol in request_volumes:
|
53 |
-
result =
|
54 |
req_vol,
|
55 |
execution_time,
|
56 |
active_cost,
|
57 |
flex_cost,
|
58 |
cold_start,
|
59 |
-
|
|
|
60 |
)
|
61 |
results.append({
|
62 |
'requests_per_hour': req_vol,
|
63 |
'monthly_requests': result['monthly_requests'],
|
64 |
'active_cost': result['active_cost'],
|
65 |
'flex_cost': result['flex_cost'],
|
66 |
-
'total_cost': result['total_monthly_cost']
|
|
|
67 |
})
|
68 |
|
69 |
df = pd.DataFrame(results)
|
@@ -103,16 +125,18 @@ def calculate_specific_cost(
|
|
103 |
execution_time,
|
104 |
active_cost,
|
105 |
flex_cost,
|
106 |
-
|
107 |
-
cold_start
|
|
|
108 |
):
|
109 |
-
result =
|
110 |
requests,
|
111 |
execution_time,
|
112 |
active_cost,
|
113 |
flex_cost,
|
114 |
cold_start,
|
115 |
-
|
|
|
116 |
)
|
117 |
|
118 |
# Format the output HTML for the table
|
@@ -126,6 +150,14 @@ def calculate_specific_cost(
|
|
126 |
<td style="padding: 8px; border: 1px solid #ddd;">Monthly Requests</td>
|
127 |
<td style="padding: 8px; text-align: right; border: 1px solid #ddd;">{result['monthly_requests']:,.0f}</td>
|
128 |
</tr>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
129 |
<tr>
|
130 |
<td style="padding: 8px; border: 1px solid #ddd;">Active Worker Cost</td>
|
131 |
<td style="padding: 8px; text-align: right; border: 1px solid #ddd;">${result['active_cost']:,.2f}</td>
|
@@ -138,11 +170,16 @@ def calculate_specific_cost(
|
|
138 |
<td style="padding: 8px; border: 1px solid #ddd;">Total Monthly Cost</td>
|
139 |
<td style="padding: 8px; text-align: right; border: 1px solid #ddd;">${result['total_monthly_cost']:,.2f}</td>
|
140 |
</tr>
|
|
|
|
|
|
|
|
|
141 |
</table>
|
142 |
"""
|
143 |
|
144 |
return html
|
145 |
|
|
|
146 |
theme = gr.themes.Default(
|
147 |
primary_hue=gr.themes.colors.red,
|
148 |
secondary_hue=gr.themes.colors.red,
|
@@ -161,8 +198,9 @@ with gr.Blocks(title="RunPod Serverless Cost Estimator", theme=theme) as demo:
|
|
161 |
execution_time = gr.Slider(minimum=1, maximum=120, value=30, step=1, label="Execution Time per Request (seconds)")
|
162 |
active_cost = gr.Number(value=0.00019, label="Active Worker Cost ($/second)", precision=5)
|
163 |
flex_cost = gr.Number(value=0.00031, label="Flex Worker Cost ($/second)", precision=5)
|
164 |
-
|
165 |
-
cold_start = gr.Slider(minimum=0, maximum=60, value=
|
|
|
166 |
|
167 |
plot_button = gr.Button("Generate Cost Projection")
|
168 |
|
@@ -181,13 +219,13 @@ with gr.Blocks(title="RunPod Serverless Cost Estimator", theme=theme) as demo:
|
|
181 |
|
182 |
plot_button.click(
|
183 |
generate_cost_projection,
|
184 |
-
inputs=[min_requests, max_requests, execution_time, active_cost, flex_cost,
|
185 |
outputs=plot_output
|
186 |
)
|
187 |
|
188 |
calc_button.click(
|
189 |
calculate_specific_cost,
|
190 |
-
inputs=[specific_requests, execution_time, active_cost, flex_cost,
|
191 |
outputs=cost_table
|
192 |
)
|
193 |
|
|
|
3 |
import pandas as pd
|
4 |
import numpy as np
|
5 |
|
6 |
+
def calculate_runpod_cost_revised(
|
7 |
requests_per_hour,
|
8 |
execution_time_per_request,
|
9 |
active_cost_per_second,
|
10 |
flex_cost_per_second,
|
11 |
+
cold_start_penalty,
|
12 |
+
num_active_workers,
|
13 |
+
concurrent_batch_size=1 # How many requests can be processed simultaneously by one worker
|
14 |
):
|
15 |
+
# Monthly hours and requests
|
16 |
+
monthly_hours = 24 * 30
|
17 |
+
seconds_per_hour = 3600
|
18 |
+
monthly_requests = requests_per_hour * monthly_hours
|
19 |
|
20 |
+
# Calculate active worker capacity (requests per hour)
|
21 |
+
# Each worker can process (concurrent_batch_size) requests at a time
|
22 |
+
active_capacity_per_hour = (seconds_per_hour / execution_time_per_request) * num_active_workers * concurrent_batch_size
|
23 |
|
24 |
+
# Total requests handled by active workers over the month
|
25 |
+
active_requests_handled = min(monthly_requests, active_capacity_per_hour * monthly_hours)
|
26 |
|
27 |
+
# Remaining requests go to flex workers
|
28 |
+
flex_requests = max(0, monthly_requests - active_requests_handled)
|
29 |
+
|
30 |
+
# Active worker cost (constant regardless of actual usage)
|
31 |
+
active_cost = num_active_workers * active_cost_per_second * seconds_per_hour * monthly_hours
|
32 |
+
|
33 |
+
# For flex workers, we calculate cold starts based on request arrival pattern
|
34 |
+
# This is simplified - in reality depends on actual traffic patterns
|
35 |
+
avg_requests_per_cold_start = concurrent_batch_size * 2 # Estimate that batches arrive close enough to reuse some workers
|
36 |
+
cold_starts = flex_requests / avg_requests_per_cold_start if flex_requests > 0 else 0
|
37 |
+
|
38 |
+
# Flex worker cost (only pay for processing time + cold starts)
|
39 |
+
flex_processing_cost = flex_requests * execution_time_per_request * flex_cost_per_second
|
40 |
+
flex_cold_start_cost = cold_starts * cold_start_penalty * flex_cost_per_second
|
41 |
+
flex_cost = flex_processing_cost + flex_cold_start_cost
|
42 |
+
|
43 |
+
# Active worker utilization
|
44 |
+
active_utilization = (active_requests_handled / (active_capacity_per_hour * monthly_hours) * 100) if num_active_workers > 0 else 0
|
45 |
|
|
|
46 |
total_monthly_cost = active_cost + flex_cost
|
47 |
|
48 |
return {
|
49 |
"monthly_requests": monthly_requests,
|
50 |
+
"active_requests": active_requests_handled,
|
51 |
"flex_requests": flex_requests,
|
52 |
"active_cost": active_cost,
|
53 |
"flex_cost": flex_cost,
|
54 |
+
"total_monthly_cost": total_monthly_cost,
|
55 |
+
"active_utilization": active_utilization
|
56 |
}
|
57 |
|
58 |
def generate_cost_projection(
|
|
|
61 |
execution_time,
|
62 |
active_cost,
|
63 |
flex_cost,
|
64 |
+
num_active_workers,
|
65 |
+
cold_start,
|
66 |
+
concurrent_batch_size
|
67 |
):
|
68 |
# Generate data for different request volumes
|
69 |
request_volumes = np.linspace(min_requests, max_requests, 20)
|
70 |
results = []
|
71 |
|
72 |
for req_vol in request_volumes:
|
73 |
+
result = calculate_runpod_cost_revised(
|
74 |
req_vol,
|
75 |
execution_time,
|
76 |
active_cost,
|
77 |
flex_cost,
|
78 |
cold_start,
|
79 |
+
num_active_workers,
|
80 |
+
concurrent_batch_size
|
81 |
)
|
82 |
results.append({
|
83 |
'requests_per_hour': req_vol,
|
84 |
'monthly_requests': result['monthly_requests'],
|
85 |
'active_cost': result['active_cost'],
|
86 |
'flex_cost': result['flex_cost'],
|
87 |
+
'total_cost': result['total_monthly_cost'],
|
88 |
+
'active_utilization': result['active_utilization']
|
89 |
})
|
90 |
|
91 |
df = pd.DataFrame(results)
|
|
|
125 |
execution_time,
|
126 |
active_cost,
|
127 |
flex_cost,
|
128 |
+
num_active_workers,
|
129 |
+
cold_start,
|
130 |
+
concurrent_batch_size
|
131 |
):
|
132 |
+
result = calculate_runpod_cost_revised(
|
133 |
requests,
|
134 |
execution_time,
|
135 |
active_cost,
|
136 |
flex_cost,
|
137 |
cold_start,
|
138 |
+
num_active_workers,
|
139 |
+
concurrent_batch_size
|
140 |
)
|
141 |
|
142 |
# Format the output HTML for the table
|
|
|
150 |
<td style="padding: 8px; border: 1px solid #ddd;">Monthly Requests</td>
|
151 |
<td style="padding: 8px; text-align: right; border: 1px solid #ddd;">{result['monthly_requests']:,.0f}</td>
|
152 |
</tr>
|
153 |
+
<tr>
|
154 |
+
<td style="padding: 8px; border: 1px solid #ddd;">Requests Handled by Active Workers</td>
|
155 |
+
<td style="padding: 8px; text-align: right; border: 1px solid #ddd;">{result['active_requests']:,.0f}</td>
|
156 |
+
</tr>
|
157 |
+
<tr>
|
158 |
+
<td style="padding: 8px; border: 1px solid #ddd;">Requests Handled by Flex Workers</td>
|
159 |
+
<td style="padding: 8px; text-align: right; border: 1px solid #ddd;">{result['flex_requests']:,.0f}</td>
|
160 |
+
</tr>
|
161 |
<tr>
|
162 |
<td style="padding: 8px; border: 1px solid #ddd;">Active Worker Cost</td>
|
163 |
<td style="padding: 8px; text-align: right; border: 1px solid #ddd;">${result['active_cost']:,.2f}</td>
|
|
|
170 |
<td style="padding: 8px; border: 1px solid #ddd;">Total Monthly Cost</td>
|
171 |
<td style="padding: 8px; text-align: right; border: 1px solid #ddd;">${result['total_monthly_cost']:,.2f}</td>
|
172 |
</tr>
|
173 |
+
<tr>
|
174 |
+
<td style="padding: 8px; border: 1px solid #ddd;">Active Worker Utilization</td>
|
175 |
+
<td style="padding: 8px; text-align: right; border: 1px solid #ddd;">{result['active_utilization']:.1f}%</td>
|
176 |
+
</tr>
|
177 |
</table>
|
178 |
"""
|
179 |
|
180 |
return html
|
181 |
|
182 |
+
# Create theme
|
183 |
theme = gr.themes.Default(
|
184 |
primary_hue=gr.themes.colors.red,
|
185 |
secondary_hue=gr.themes.colors.red,
|
|
|
198 |
execution_time = gr.Slider(minimum=1, maximum=120, value=30, step=1, label="Execution Time per Request (seconds)")
|
199 |
active_cost = gr.Number(value=0.00019, label="Active Worker Cost ($/second)", precision=5)
|
200 |
flex_cost = gr.Number(value=0.00031, label="Flex Worker Cost ($/second)", precision=5)
|
201 |
+
num_active_workers = gr.Slider(minimum=0, maximum=600, value=30, step=10, label="Number of Active Workers")
|
202 |
+
cold_start = gr.Slider(minimum=0, maximum=60, value=1, step=1, label="Cold Start Penalty (seconds)")
|
203 |
+
concurrent_batch_size = gr.Slider(minimum=1, maximum=10, value=2, step=1, label="Concurrent Requests per Worker")
|
204 |
|
205 |
plot_button = gr.Button("Generate Cost Projection")
|
206 |
|
|
|
219 |
|
220 |
plot_button.click(
|
221 |
generate_cost_projection,
|
222 |
+
inputs=[min_requests, max_requests, execution_time, active_cost, flex_cost, num_active_workers, cold_start, concurrent_batch_size],
|
223 |
outputs=plot_output
|
224 |
)
|
225 |
|
226 |
calc_button.click(
|
227 |
calculate_specific_cost,
|
228 |
+
inputs=[specific_requests, execution_time, active_cost, flex_cost, num_active_workers, cold_start, concurrent_batch_size],
|
229 |
outputs=cost_table
|
230 |
)
|
231 |
|