SciPy Image Convolution Benchmark
This little benchmark performs an image convolution in parallel using SciPy’s
signal.convolve2d
call. It can be run with dragon
or standard python3
to
compare performance on your machine.
The code demonstrates the following key concepts working with Dragon:
How to write programs that can run with either: Dragon or standard Python3
How to use
multiprocessing.Pool
to parallelize scientific workloads
1import os
2import time
3import numpy as np
4
5import dragon
6import multiprocessing as mp
7from scipy import signal
8
9NUM_WORKERS = 4
10IMAGE_SIZE = 1024
11MEMSIZE = 32 * 1024**2 # 32 MB
12NUM_ITER = 10
13NUM_BURN = 2
14
15
16def convolve_image_filter(args: tuple) -> np.ndarray:
17 """Use scipy to convolve an image with a filter
18
19 :param args: tuple containing image and filter
20 :type args: tuple
21 :return: convolved image
22 :rtype: np.array
23 """
24 image, random_filter = args
25 return signal.convolve2d(image, random_filter)[::5, ::5]
26
27
28if __name__ == "__main__":
29
30 # check if we should use the Dragon
31 if "DRAGON_PATCH_MP" in os.environ:
32 mp.set_start_method("dragon")
33 else:
34 mp.set_start_method("spawn")
35
36 # create the worker pool
37 pool = mp.Pool(NUM_WORKERS)
38
39 # create images, stay within MEMSIZE
40 image = np.zeros((IMAGE_SIZE, IMAGE_SIZE))
41 num_images = int(float(MEMSIZE) / float(image.size))
42
43 rng = np.random.default_rng(42)
44 filters = [rng.standard_normal(size=(4, 4)) for _ in range(num_images)]
45 images = [np.zeros((IMAGE_SIZE, IMAGE_SIZE)) for _ in range(num_images)]
46
47 print(f"# of workers = {NUM_WORKERS}", flush=True)
48 print(f"memory footprint = {MEMSIZE}", flush=True)
49 print(f"# of images = {num_images}", flush=True)
50
51 # run a small benchmark
52
53 results = []
54 for _ in range(NUM_BURN + NUM_ITER):
55 beg = time.perf_counter()
56 pool.map(convolve_image_filter, zip(images, filters))
57 end = time.perf_counter()
58 results.append(end - beg)
59
60 results = results[NUM_BURN:] # throw away burn in results
61
62 pool.close()
63 pool.join()
64 print(f"Average execution time {round(np.mean(results), 2)} second(s)", flush=True)
65 print(f"Standard deviation: {round(np.std(results), 2)} second(s)")
This code can be run either with standard Python3 with python3 scipy_image_demo.py
:
1>$python3 scipy_image_demo.py
2# of workers = 4
3memory footprint = 33554432
4# of images = 32
5Average execution time 0.73 second(s)
6Standard deviation: 0.03 second(s)
or using the Dragon runtime, potentially also on multiple nodes with dragon scipy_image_demo.py
:
1>$dragon scipy_image_demo.py
2# of workers = 4
3memory footprint = 33554432
4# of images = 32
5Average execution time 1.02 second(s)
6Standard deviation: 0.2 second(s)
7+++ head proc exited, code 0