-
Notifications
You must be signed in to change notification settings - Fork 68
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Building support for local deployment #142
base: master
Are you sure you want to change the base?
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -46,21 +46,22 @@ def prepare(self, sebs_client: "SeBS", deployment_client: FaaSSystem): | |
self._benchmark = sebs_client.get_benchmark( | ||
settings["benchmark"], deployment_client, self.config | ||
) | ||
self._function = deployment_client.get_function(self._benchmark) | ||
self._functions = deployment_client.get_function(self._benchmark, 3) | ||
# prepare benchmark input | ||
self._storage = deployment_client.get_storage(replace_existing=self.config.update_storage) | ||
self._benchmark_input = self._benchmark.prepare_input( | ||
storage=self._storage, size=settings["input-size"] | ||
) | ||
for i in range(len(self._functions)): | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. As mentioned above - we should have one function instance, and the Local instance and its triggers will allocate many Docker containers. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I imagine this seems more complex than anticipated since cloud platforms expose an HTTP trigger, and the local deployment would have a different HTTP address for each instance. We don't want to have to change every experiment - we want to hide this complexity behind the trigger. The simplest solution would be to implement a new |
||
|
||
# add HTTP trigger | ||
triggers = self._function.triggers(Trigger.TriggerType.HTTP) | ||
if len(triggers) == 0: | ||
self._trigger = deployment_client.create_trigger( | ||
self._function, Trigger.TriggerType.HTTP | ||
) | ||
else: | ||
self._trigger = triggers[0] | ||
# add HTTP trigger | ||
triggers = self._functions[i].triggers(Trigger.TriggerType.HTTP) | ||
if len(triggers) == 0: | ||
self._trigger = deployment_client.create_trigger( | ||
self._functions[i], Trigger.TriggerType.HTTP | ||
) | ||
else: | ||
self._trigger = triggers[0] | ||
|
||
self._out_dir = os.path.join(sebs_client.output_dir, "perf-cost") | ||
if not os.path.exists(self._out_dir): | ||
|
@@ -77,15 +78,15 @@ def run(self): | |
if len(memory_sizes) == 0: | ||
self.logging.info("Begin experiment") | ||
self.run_configuration(settings, settings["repetitions"]) | ||
for memory in memory_sizes: | ||
self.logging.info(f"Begin experiment on memory size {memory}") | ||
self._function.memory = memory | ||
self._deployment_client.update_function(self._function, self._benchmark) | ||
self._sebs_client.cache_client.update_function(self._function) | ||
self.run_configuration(settings, settings["repetitions"], suffix=str(memory)) | ||
for i in range(len(self._functions)): | ||
for memory in memory_sizes: | ||
self.logging.info(f"Begin experiment on memory size {memory}") | ||
self._functions[i].memory = memory | ||
self._deployment_client.update_function(self._functions[i], self._benchmark) | ||
self._sebs_client.cache_client.update_function(self._functions[i]) | ||
self.run_configuration(settings, settings["repetitions"], suffix=str(memory)) | ||
|
||
def compute_statistics(self, times: List[float]): | ||
|
||
mean, median, std, cv = basic_stats(times) | ||
self.logging.info(f"Mean {mean} [ms], median {median} [ms], std {std}, CV {cv}") | ||
for alpha in [0.95, 0.99]: | ||
|
@@ -154,9 +155,8 @@ def _run_configuration( | |
|
||
if run_type == PerfCost.RunType.COLD or run_type == PerfCost.RunType.BURST: | ||
self._deployment_client.enforce_cold_start( | ||
[self._function], self._benchmark | ||
self._functions, self._benchmark | ||
) | ||
|
||
time.sleep(5) | ||
|
||
results = [] | ||
|
@@ -179,7 +179,8 @@ def _run_configuration( | |
elif run_type == PerfCost.RunType.WARM and ret.stats.cold_start: | ||
self.logging.info(f"Invocation {ret.request_id} is cold!") | ||
else: | ||
result.add_invocation(self._function, ret) | ||
for i in range(len(self._functions)): | ||
result.add_invocation(self._functions[i], ret) | ||
colds_count += ret.stats.cold_start | ||
client_times.append(ret.times.client / 1000.0) | ||
samples_gathered += 1 | ||
|
@@ -224,7 +225,6 @@ def _run_configuration( | |
) | ||
|
||
def run_configuration(self, settings: dict, repetitions: int, suffix: str = ""): | ||
|
||
for experiment_type in settings["experiments"]: | ||
if experiment_type == "cold": | ||
self._run_configuration( | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -115,7 +115,7 @@ def package_code( | |
pass | ||
|
||
@abstractmethod | ||
def create_function(self, code_package: Benchmark, func_name: str) -> Function: | ||
def create_function(self, code_package: Benchmark, func_name: str, num: int) -> Function: | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We shouldn't change this API There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I had added the |
||
pass | ||
|
||
@abstractmethod | ||
|
@@ -139,7 +139,7 @@ def update_function(self, function: Function, code_package: Benchmark): | |
|
||
""" | ||
|
||
def get_function(self, code_package: Benchmark, func_name: Optional[str] = None) -> Function: | ||
def get_function(self, code_package: Benchmark, num: int, func_name: Optional[str] = None) -> List[Function]: | ||
|
||
if code_package.language_version not in self.system_config.supported_language_versions( | ||
self.name(), code_package.language_name | ||
|
@@ -171,15 +171,16 @@ def get_function(self, code_package: Benchmark, func_name: Optional[str] = None) | |
else "function {} not found in cache.".format(func_name) | ||
) | ||
self.logging.info("Creating new function! Reason: " + msg) | ||
function = self.create_function(code_package, func_name) | ||
self.cache_client.add_function( | ||
deployment_name=self.name(), | ||
language_name=code_package.language_name, | ||
code_package=code_package, | ||
function=function, | ||
) | ||
code_package.query_cache() | ||
return function | ||
function_list = self.create_function(code_package, func_name, num) | ||
for function in function_list: | ||
self.cache_client.add_function( | ||
deployment_name=self.name(), | ||
language_name=code_package.language_name, | ||
code_package=code_package, | ||
function=function, | ||
) | ||
code_package.query_cache() | ||
return function_list | ||
else: | ||
# retrieve function | ||
cached_function = functions[func_name] | ||
|
@@ -221,7 +222,9 @@ def get_function(self, code_package: Benchmark, func_name: Optional[str] = None) | |
code_package.query_cache() | ||
else: | ||
self.logging.info(f"Cached function {func_name} is up to date.") | ||
return function | ||
function_list = [] | ||
function_list.append(function) | ||
return function_list | ||
|
||
@abstractmethod | ||
def update_function_configuration(self, cached_function: Function, benchmark: Benchmark): | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -99,6 +99,7 @@ def deserialize(cached_config: dict) -> "LocalFunction": | |
) | ||
except docker.errors.NotFound: | ||
raise RuntimeError(f"Cached container {instance_id} not available anymore!") | ||
# clear cache | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. FIXME? |
||
|
||
def stop(self): | ||
self.logging.info(f"Stopping function container {self._instance_id}") | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The allocation of multiple function instances should happen in the deployment.
For example, AWS/GCP/Azure will create instances for us automatically. In Local, you need to allocate them as requested by spawning more Docker containers hosting the function.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Furthermore, the number 3 should not be hardcoded anywhere.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry I had done this for testing purpose but missed to remove it before pushing. I will remove it.