Skip to content

Commit

Permalink
feat: add execution time threshold handling. (#133)
Browse files Browse the repository at this point in the history
* chore: add execution time threshold handling.

* Format with Black

* fix: fixing the execution time threshold handling

Format with Black

---------

Co-authored-by: Github Actions Bot <>
  • Loading branch information
MehdiBC authored Jun 2, 2023
1 parent a4c2040 commit 96302d0
Show file tree
Hide file tree
Showing 7 changed files with 62 additions and 14 deletions.
5 changes: 4 additions & 1 deletion spot/Spot.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,10 @@ def __init__(self, config_dir: str, aws_session):
self.ctx, aws_session, self.config.function_name
)
self.recommendation_engine = RecommendationEngine(
function_invoker, self.payload_file_path, self.config.mem_bounds
function_invoker,
self.payload_file_path,
self.config.mem_bounds,
self.config.execution_time_threshold,
)
self.benchmark_name = os.path.basename(config_dir)

Expand Down
3 changes: 3 additions & 0 deletions spot/benchmark_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ def __init__(self, f=None):
self.mem_size: int
self.mem_bounds: list
self.nickname: str
self.execution_time_threshold: float

if f is not None:
self.deserialize(f)
Expand All @@ -29,6 +30,7 @@ def _set_properties(
region: str,
random_seed: int,
nickname: str,
execution_time_threshold: float = None,
mem_bounds: Optional[list] = None,
):
self.function_name = function_name
Expand All @@ -37,6 +39,7 @@ def _set_properties(
self.mem_bounds = DEFAULT_MEM_BOUNDS if mem_bounds is None else mem_bounds
self.random_seed = random_seed
self.nickname = nickname
self.execution_time_threshold = execution_time_threshold

def deserialize(self, f):
try:
Expand Down
2 changes: 2 additions & 0 deletions spot/exceptions/no_memory_left.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
class NoMemoryLeft(Exception):
pass
2 changes: 1 addition & 1 deletion spot/invocation/aws_lambda_invoker.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
import sys
import time

import botocore
import botocore.exceptions
import re
import pandas as pd
from concurrent.futures import ThreadPoolExecutor
Expand Down
2 changes: 1 addition & 1 deletion spot/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ def main():
help="Return best memory configuration for lowest cost",
)
parser.add_argument(
"--force", "-f", action="store_true", help="Ignore the invokation cache"
"--force", "-f", action="store_true", help="Ignore the invocation cache"
)
parser.add_argument(
"--invoke", "-i", type=int, help="The number of times you invoke the function"
Expand Down
41 changes: 31 additions & 10 deletions spot/recommendation_engine/recommendation_engine.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
import sys

import numpy as np
import pandas as pd

from spot.exceptions.no_memory_left import NoMemoryLeft
from spot.recommendation_engine.objectives import *
from spot.recommendation_engine.utility import Utility
from spot.constants import *
Expand All @@ -14,13 +17,20 @@ def __init__(self, memory, billed_time):


class RecommendationEngine:
def __init__(self, invocator, payload_path, memory_range):
def __init__(
self,
invocator,
payload_path,
memory_range,
execution_time_threshold: float = None,
):
self.payload_path = payload_path
self.function_invocator = invocator
self.sampled_datapoints = []
self.fitted_function = None
self.function_parameters = {}
self.memory_range = memory_range
self.execution_time_threshold = execution_time_threshold
if OPTIMIZATION_OBJECTIVE == "normal":
self.objective = NormalObjective(self, self.memory_range)
elif OPTIMIZATION_OBJECTIVE == "fit_to_real_cost":
Expand Down Expand Up @@ -50,15 +60,26 @@ def run(self):
return self.report()

def report(self):
minimum_memory, minimum_cost = Utility.find_minimum_memory_cost(
self.fitted_function, self.function_parameters, self.memory_range
)
result = {
"Minimum Cost Memory": [minimum_memory],
"Expected Cost": [minimum_cost],
"Exploration Cost": [self.exploration_cost],
}
return pd.DataFrame.from_dict(result)
try:
minimum_memory, minimum_cost = Utility.find_minimum_memory_cost(
self.fitted_function,
self.function_parameters,
self.memory_range,
self.execution_time_threshold,
)
result = {
"Minimum Cost Memory": [minimum_memory],
"Expected Cost": [minimum_cost],
"Exploration Cost": [self.exploration_cost],
}
return pd.DataFrame.from_dict(result)

except NoMemoryLeft:
print(
"No memory configuration is possible. The execution time threshold is too low!",
file=sys.stderr,
)
exit(1)

def initial_sample(self):
# TODO: ensure initial memories are in the accepted memory range.
Expand Down
21 changes: 20 additions & 1 deletion spot/recommendation_engine/utility.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,31 @@
import numpy as np
from scipy.optimize import curve_fit

from spot.exceptions.no_memory_left import NoMemoryLeft


class Utility:
@staticmethod
def find_minimum_memory_cost(f, params, memory_range):
def find_minimum_memory_cost(
f, params, memory_range, execution_time_threshold: float = None
):
mems = np.arange(memory_range[0], memory_range[1] + 1, dtype=np.double)
costs = f(mems, *params)

# Handling execution threshold
if execution_time_threshold is not None:
filtered_mems = np.array([])
filtered_costs = np.array([])
execution_times = costs / mems
for i in range(len(execution_times)):
if execution_times[i] <= execution_time_threshold:
filtered_mems = np.append(filtered_mems, mems[i])
filtered_costs = np.append(filtered_costs, costs[i])
mems = filtered_mems
costs = filtered_costs
if len(mems) == 0:
raise NoMemoryLeft()

min_index = np.argmin(costs)
return mems[min_index], costs[min_index]

Expand Down

0 comments on commit 96302d0

Please sign in to comment.