Skip to content

Commit bd9d5cb

Browse files
authored
Add the ability to benchmark throughput using multiple threads (#359)
1 parent 52d0ec6 commit bd9d5cb

File tree

2 files changed

+117
-24
lines changed

2 files changed

+117
-24
lines changed

Diff for: benchmarks/decoders/benchmark_decoders.py

+2
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313

1414
from benchmark_decoders_library import (
1515
AbstractDecoder,
16+
BatchParameters,
1617
DecordAccurate,
1718
DecordAccurateBatch,
1819
plot_data,
@@ -173,6 +174,7 @@ def main() -> None:
173174
num_sequential_frames_from_start=[1, 10, 100],
174175
min_runtime_seconds=args.bm_video_speed_min_run_seconds,
175176
benchmark_video_creation=args.bm_video_creation,
177+
batch_parameters=BatchParameters(num_threads=8, batch_size=40),
176178
)
177179
plot_data(df_data, args.plot_path)
178180

Diff for: benchmarks/decoders/benchmark_decoders_library.py

+115-24
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import subprocess
44
import urllib.request
55
from concurrent.futures import ThreadPoolExecutor, wait
6+
from dataclasses import dataclass
67
from itertools import product
78
from pathlib import Path
89

@@ -479,13 +480,51 @@ def get_metadata(video_file_path: str) -> VideoStreamMetadata:
479480
return VideoDecoder(video_file_path).metadata
480481

481482

483+
@dataclass
484+
class BatchParameters:
485+
num_threads: int
486+
batch_size: int
487+
488+
489+
def run_batch_using_threads(
490+
function,
491+
*args,
492+
batch_parameters: BatchParameters = BatchParameters(num_threads=8, batch_size=40),
493+
):
494+
executor = ThreadPoolExecutor(max_workers=batch_parameters.num_threads)
495+
futures = []
496+
for _ in range(batch_parameters.batch_size):
497+
futures.append(executor.submit(function, *args))
498+
for f in futures:
499+
assert f.result()
500+
executor.shutdown(wait=True)
501+
502+
503+
def convert_result_to_df_item(
504+
result, decoder_name, video_file_path, num_samples, decode_pattern
505+
):
506+
df_item = {}
507+
df_item["decoder"] = decoder_name
508+
df_item["video"] = str(video_file_path)
509+
df_item["description"] = result.description
510+
df_item["frame_count"] = num_samples
511+
df_item["median"] = result.median
512+
df_item["iqr"] = result.iqr
513+
df_item["type"] = decode_pattern
514+
df_item["fps_median"] = num_samples / result.median
515+
df_item["fps_p75"] = num_samples / result._p75
516+
df_item["fps_p25"] = num_samples / result._p25
517+
return df_item
518+
519+
482520
def run_benchmarks(
483521
decoder_dict: dict[str, AbstractDecoder],
484522
video_files_paths: list[Path],
485523
num_samples: int,
486524
num_sequential_frames_from_start: list[int],
487525
min_runtime_seconds: float,
488526
benchmark_video_creation: bool,
527+
batch_parameters: BatchParameters = None,
489528
) -> list[dict[str, str | float | int]]:
490529
# Ensure that we have the same seed across benchmark runs.
491530
torch.manual_seed(0)
@@ -532,18 +571,44 @@ def run_benchmarks(
532571
results.append(
533572
seeked_result.blocked_autorange(min_run_time=min_runtime_seconds)
534573
)
535-
df_item = {}
536-
df_item["decoder"] = decoder_name
537-
df_item["video"] = str(video_file_path)
538-
df_item["description"] = results[-1].description
539-
df_item["frame_count"] = num_samples
540-
df_item["median"] = results[-1].median
541-
df_item["iqr"] = results[-1].iqr
542-
df_item["type"] = f"{kind}:seek()+next()"
543-
df_item["fps_median"] = num_samples / results[-1].median
544-
df_item["fps_p75"] = num_samples / results[-1]._p75
545-
df_item["fps_p25"] = num_samples / results[-1]._p25
546-
df_data.append(df_item)
574+
df_data.append(
575+
convert_result_to_df_item(
576+
results[-1],
577+
decoder_name,
578+
video_file_path,
579+
num_samples,
580+
f"{kind} seek()+next()",
581+
)
582+
)
583+
584+
if batch_parameters:
585+
seeked_result = benchmark.Timer(
586+
stmt="run_batch_using_threads(decoder.get_frames_from_video, video_file, pts_list, batch_parameters=batch_parameters)",
587+
globals={
588+
"video_file": str(video_file_path),
589+
"pts_list": pts_list,
590+
"decoder": decoder,
591+
"run_batch_using_threads": run_batch_using_threads,
592+
"batch_parameters": batch_parameters,
593+
},
594+
label=f"video={video_file_path} {metadata_label}",
595+
sub_label=decoder_name,
596+
description=f"batch {kind} {num_samples} seek()+next()",
597+
)
598+
results.append(
599+
seeked_result.blocked_autorange(
600+
min_run_time=min_runtime_seconds
601+
)
602+
)
603+
df_data.append(
604+
convert_result_to_df_item(
605+
results[-1],
606+
decoder_name,
607+
video_file_path,
608+
num_samples * batch_parameters.batch_size,
609+
f"batch {kind} seek()+next()",
610+
)
611+
)
547612

548613
for num_consecutive_nexts in num_sequential_frames_from_start:
549614
consecutive_frames_result = benchmark.Timer(
@@ -562,18 +627,44 @@ def run_benchmarks(
562627
min_run_time=min_runtime_seconds
563628
)
564629
)
565-
df_item = {}
566-
df_item["decoder"] = decoder_name
567-
df_item["video"] = str(video_file_path)
568-
df_item["description"] = results[-1].description
569-
df_item["frame_count"] = num_consecutive_nexts
570-
df_item["median"] = results[-1].median
571-
df_item["iqr"] = results[-1].iqr
572-
df_item["type"] = "next()"
573-
df_item["fps_median"] = num_consecutive_nexts / results[-1].median
574-
df_item["fps_p75"] = num_consecutive_nexts / results[-1]._p75
575-
df_item["fps_p25"] = num_consecutive_nexts / results[-1]._p25
576-
df_data.append(df_item)
630+
df_data.append(
631+
convert_result_to_df_item(
632+
results[-1],
633+
decoder_name,
634+
video_file_path,
635+
num_consecutive_nexts,
636+
f"{num_consecutive_nexts} next()",
637+
)
638+
)
639+
640+
if batch_parameters:
641+
consecutive_frames_result = benchmark.Timer(
642+
stmt="run_batch_using_threads(decoder.get_consecutive_frames_from_video, video_file, consecutive_frames_to_extract, batch_parameters=batch_parameters)",
643+
globals={
644+
"video_file": str(video_file_path),
645+
"consecutive_frames_to_extract": num_consecutive_nexts,
646+
"decoder": decoder,
647+
"run_batch_using_threads": run_batch_using_threads,
648+
"batch_parameters": batch_parameters,
649+
},
650+
label=f"video={video_file_path} {metadata_label}",
651+
sub_label=decoder_name,
652+
description=f"batch {num_consecutive_nexts} next()",
653+
)
654+
results.append(
655+
consecutive_frames_result.blocked_autorange(
656+
min_run_time=min_runtime_seconds
657+
)
658+
)
659+
df_data.append(
660+
convert_result_to_df_item(
661+
results[-1],
662+
decoder_name,
663+
video_file_path,
664+
num_consecutive_nexts * batch_parameters.batch_size,
665+
f"batch {num_consecutive_nexts} next()",
666+
)
667+
)
577668

578669
first_video_file_path = video_files_paths[0]
579670
if benchmark_video_creation:

0 commit comments

Comments
 (0)