diff --git a/exercises/exerciseC/plot.py b/exercises/exerciseC/plot.py new file mode 100644 index 0000000..bd4872a --- /dev/null +++ b/exercises/exerciseC/plot.py @@ -0,0 +1,67 @@ +import sys +import collections +import numpy as np + +dts = collections.defaultdict(dict) + +for fname in sys.argv[1:]: + values = open(fname).read().split() + n1 = int(values[0]) + n2 = int(values[1]) + dt = float(values[2]) + + dts[n1][n2] = dt + +print(dts) +N1 = max(dts) +N2 = max(max(v) for v in dts.values()) + +print(N1, N2) + +x = np.empty((N1 + 1, N2 + 1)) +for n1, values in dts.items(): + for n2, v in values.items(): + x[n1, n2] = v + +x[:, 0] = np.nan +x[0, :] = np.nan + +print(x) + +from matplotlib import pyplot + +fig, axes = pyplot.subplots() +im = axes.imshow(x, origin='lower') +axes.set_ylabel('# processes') +axes.set_xlabel('# threads') +axes.spines[['right', 'top']].set_visible(False) +axes.set_title('time') +fig.colorbar(im, ax=axes, label='s') + +fig_small, axes = pyplot.subplots() +im = axes.imshow(x[:5, :5], origin='lower') +axes.set_ylabel('# processes') +axes.set_xlabel('# threads') +axes.spines[['right', 'top']].set_visible(False) +axes.set_title('time') +fig_small.colorbar(im, ax=axes, label='s') + +workers = np.arange(N1 + 1)[:, None] * np.arange(N2 + 1) +speedup = x[1,1] / x + +speedup[:, 0] = np.nan +speedup[0, :] = np.nan + +figs, axes = pyplot.subplots() +im = axes.imshow(speedup, origin='lower') +axes.set_ylabel('# processes') +axes.set_xlabel('# threads') +axes.spines[['right', 'top']].set_visible(False) +axes.set_title('speedup') +figs.colorbar(im, ax=axes, label='s') + +fig.savefig('time.svg') +fig_small.savefig('time_inset.svg') +figs.savefig('speedup.svg') + +pyplot.show() diff --git a/exercises/exerciseC/process_images.py b/exercises/exerciseC/process_images.py new file mode 100644 index 0000000..0aa76dd --- /dev/null +++ b/exercises/exerciseC/process_images.py @@ -0,0 +1,69 @@ +import os +import sys +from multiprocessing import Pool as ProcessPool +import time + +def process_image(fname): + n_threads = os.getenv('OMP_NUM_THREADS', '(unset)') + print(f"Worker {fname=} OMP_NUM_THREADS={n_threads}") + + # An image is an array with width, height and three (RGB) color channels + # (Sometimes there is a transparency channel too: RGBA) + im = Image.open(fname) + try: + A = np.median(im, axis=2)[::4, ::4] + except: + A = np.array(im)[::4, ::4] + + # Decompose image + U, S, Vh = np.linalg.svd(A) + + # Remove first singular value + S[0] = 0 + smat = np.zeros(A.shape, dtype=complex) + smat[:min(A.shape), :min(A.shape)] = np.diag(S) + + # Re-compose image + A = np.dot(U, np.dot(smat, Vh)).real + A = (256*(A - A.min())/A.max()).astype('uint8') + + return A + + +if __name__ == '__main__': + n_processes = int(sys.argv[1]) + n_threads = int(sys.argv[2]) + fnames = sys.argv[3:] + + # Check that the output folders exist, or create them if needed + if not os.path.isdir('processed_images'): os.mkdir('processed_images') + if not os.path.isdir('timings'): os.mkdir('timings') + + print(f"Controller with {n_processes} processes and {n_threads} threads / worker") + + # The environment that is set in the parent is inherited by child workers, + # we need to set the variable before numpy is imported! + os.environ['OMP_NUM_THREADS'] = str(n_threads) + + # We delay the import of numpy because we want to set OMP_NUM_THREADS. + # We delay the import of PIL in case it uses numpy internally. + import numpy as np + from PIL import Image + + # Time the execution + start_time = time.time() + with ProcessPool(n_processes) as p: + new_images = p.map(process_image, fnames) + elapsed_time = time.time() - start_time + + for im, fname in zip(new_images, fnames): + im = Image.fromarray(im) + im.save(fname.replace('images', 'processed_images')) + + print(f'{n_processes} processes and {n_threads} threads and {len(fnames)} jobs: {elapsed_time}') + + # IO: Save the timing to a unique txt file + filename = f'timings/{n_processes:02}_processes_{n_threads:02}_threads.txt' + with open(filename, 'w') as file: + file.write(f'{n_processes} {n_threads} {elapsed_time:.6f}') + \ No newline at end of file diff --git a/exercises/exerciseC/run_with_all_configurations.sh b/exercises/exerciseC/run_with_all_configurations.sh new file mode 100644 index 0000000..60ea678 --- /dev/null +++ b/exercises/exerciseC/run_with_all_configurations.sh @@ -0,0 +1,8 @@ +# This is bash, it executes the python script multiple times +for i in {1..10} +do + for j in {1..10} + do + python process_images.py $i $j images/* + done +done \ No newline at end of file