Skip to content

Commit 066cf31

Browse files
committed
Add exec and invoke duration settings
1 parent b3f7713 commit 066cf31

7 files changed

Lines changed: 58 additions & 17 deletions

File tree

app/grandchallenge/components/backends/base.py

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -325,11 +325,15 @@ def __init__(
325325
use_warm_pool and settings.COMPONENTS_USE_WARM_POOL
326326
)
327327
self._signing_key = signing_key
328+
self._algorithm_model = algorithm_model
329+
self._ground_truth = ground_truth
330+
331+
self._exec_duration = None
332+
self._invoke_duration = None
328333
self._stdout = []
329334
self._stderr = []
335+
330336
self.__s3_client = None
331-
self._algorithm_model = algorithm_model
332-
self._ground_truth = ground_truth
333337

334338
def provision(self, *, input_civs, input_prefixes):
335339
# We cannot run everything async as it requires database access.
@@ -401,6 +405,14 @@ def stderr(self):
401405
@abstractmethod
402406
def utilization_duration(self): ...
403407

408+
@property
409+
def exec_duration(self):
410+
return self._exec_duration
411+
412+
@property
413+
def invoke_duration(self):
414+
return self._invoke_duration
415+
404416
@property
405417
@abstractmethod
406418
def usd_cents_per_hour(self): ...
@@ -866,6 +878,9 @@ def _get_inference_result(self):
866878
def _handle_completed_job(self):
867879
inference_result = self._get_inference_result()
868880

881+
self._exec_duration = inference_result.exec_duration
882+
self._invoke_duration = inference_result.invoke_duration
883+
869884
users_process_exit_code = inference_result.return_code
870885

871886
if users_process_exit_code == 0:

app/grandchallenge/components/models.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1736,7 +1736,7 @@ def save(self, *args, **kwargs):
17361736
if adding:
17371737
self.create_utilization()
17381738

1739-
def update_status(
1739+
def update_status( # noqa:C901
17401740
self,
17411741
*,
17421742
status: STATUS_CHOICES,
@@ -1745,6 +1745,8 @@ def update_status(
17451745
error_message="",
17461746
detailed_error_message=None,
17471747
utilization_duration=None,
1748+
exec_duration=None,
1749+
invoke_duration=None,
17481750
compute_cost_euro_millicents=None,
17491751
runtime_metrics=None,
17501752
):
@@ -1769,6 +1771,12 @@ def update_status(
17691771
self.utilization.duration = utilization_duration
17701772
self.utilization.save(update_fields=["duration"])
17711773

1774+
if exec_duration is not None:
1775+
self.exec_duration = exec_duration
1776+
1777+
if invoke_duration is not None:
1778+
self.invoke_duration = invoke_duration
1779+
17721780
if compute_cost_euro_millicents is not None:
17731781
self.utilization.compute_cost_euro_millicents = (
17741782
compute_cost_euro_millicents

app/grandchallenge/components/tasks.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -858,6 +858,8 @@ def get_update_status_kwargs(*, executor=None):
858858
"stdout": executor.stdout,
859859
"stderr": executor.stderr,
860860
"utilization_duration": executor.utilization_duration,
861+
"exec_duration": executor.exec_duration,
862+
"invoke_duration": executor.invoke_duration,
861863
"compute_cost_euro_millicents": executor.compute_cost_euro_millicents,
862864
"runtime_metrics": executor.runtime_metrics,
863865
}

app/tests/algorithms_tests/test_tasks.py

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -338,25 +338,26 @@ def test_algorithm(client, settings, django_capture_on_commit_callbacks):
338338
django_capture_on_commit_callbacks=django_capture_on_commit_callbacks,
339339
)
340340

341-
jobs = Job.objects.filter(algorithm_image=ai).all()
342-
343341
# There should be a single, successful job
344-
assert len(jobs) == 1
342+
job = Job.objects.filter(algorithm_image=ai).get()
345343

346-
assert jobs[0].stdout.endswith("Greetings from stdout")
347-
assert jobs[0].stderr.endswith('("Hello from stderr")')
348-
assert "UserWarning: Could not google: [Errno " in jobs[0].stderr
349-
assert jobs[0].error_message == ""
350-
assert jobs[0].status == jobs[0].SUCCESS
344+
assert job.stdout.endswith("Greetings from stdout")
345+
assert job.stderr.endswith('("Hello from stderr")')
346+
assert "UserWarning: Could not google: [Errno " in job.stderr
347+
assert job.error_message == ""
348+
assert job.status == job.SUCCESS
349+
assert job.exec_duration == timedelta(seconds=1337)
350+
assert job.invoke_duration == timedelta(seconds=1874)
351+
assert job.utilization.duration.total_seconds() > 0
351352

352353
# The job should have two ComponentInterfaceValues,
353354
# one for the results.json and one for output.tif
354-
assert len(jobs[0].outputs.all()) == 2
355+
assert len(job.outputs.all()) == 2
355356

356-
json_result_civ = jobs[0].outputs.get(interface=json_result_interface)
357+
json_result_civ = job.outputs.get(interface=json_result_interface)
357358
assert json_result_civ.value
358359

359-
heatmap_civ = jobs[0].outputs.get(interface=heatmap_interface)
360+
heatmap_civ = job.outputs.get(interface=heatmap_interface)
360361
assert heatmap_civ.image.name == "input_file.tif"
361362

362363
# We add another ComponentInterface with file value and run the algorithm again

app/tests/components_tests/resources/backends.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ def execute(self):
9090
pk=self._job_id,
9191
return_code=0,
9292
exec_duration=timedelta(seconds=1337),
93-
invoke_duration=None,
93+
invoke_duration=timedelta(seconds=1874),
9494
outputs=[],
9595
sagemaker_shim_version="0.5.0",
9696
)
@@ -124,6 +124,8 @@ def execute(self):
124124
"_job_id": self._job_id,
125125
"_stdout": self._stdout,
126126
"_stderr": self._stderr,
127+
"_exec_duration_seconds": self._exec_duration.total_seconds(),
128+
"_invoke_duration_seconds": self._invoke_duration.total_seconds(),
127129
"__start_time": self.__start_time,
128130
},
129131
"backend": f"{self.__class__.__module__}.{self.__class__.__qualname__}",
@@ -134,6 +136,12 @@ def execute(self):
134136
def handle_event(self, *, event):
135137
self._stdout = event["_stdout"]
136138
self._stderr = event["_stderr"]
139+
self._exec_duration = timedelta(
140+
seconds=event["_exec_duration_seconds"]
141+
)
142+
self._invoke_duration = timedelta(
143+
seconds=event["_invoke_duration_seconds"]
144+
)
137145
self.__start_time = event["__start_time"]
138146

139147
@staticmethod

app/tests/components_tests/test_amazon_sagemaker_training_backend.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -563,8 +563,8 @@ def test_handle_completed_job():
563563
inference_result = InferenceResult(
564564
pk=f"algorithms-job-{pk}",
565565
return_code=0,
566-
exec_duration=timedelta(seconds=1337),
567-
invoke_duration=None,
566+
exec_duration=timedelta(seconds=51432),
567+
invoke_duration=timedelta(seconds=1543),
568568
outputs=[],
569569
sagemaker_shim_version="0.5.0",
570570
)
@@ -587,6 +587,10 @@ def test_handle_completed_job():
587587

588588
assert executor._handle_completed_job() is None
589589

590+
# The durations should be set from the result object
591+
assert executor.exec_duration == timedelta(seconds=51432)
592+
assert executor.invoke_duration == timedelta(seconds=1543)
593+
590594

591595
def test_handle_time_limit_exceded(settings):
592596
settings.COMPONENTS_AMAZON_ECR_REGION = "us-east-1"

app/tests/evaluation_tests/test_tasks.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,9 @@ def test_submission_evaluation(
130130
]
131131
== 0.5
132132
)
133+
assert evaluation.exec_duration == timedelta(seconds=1337)
134+
assert evaluation.invoke_duration == timedelta(seconds=1874)
135+
assert evaluation.utilization.duration.total_seconds() > 0
133136

134137
# Try with a csv file
135138
user_upload = create_upload_from_file(

0 commit comments

Comments
 (0)