Skip to content

Vsebniki za procesorska jedra

Pri uporabi vsebnikov, ki za računanje izkoriščajo samo procesorska jedra, ne bi smeli imeti nobenih težav. Pripravljeno sliko vsebnika prenesemo na superračunalnik in jo na ustrezen način zaženemo.

Pripravimo vsebnik mb-cpu.sif

Najprej bomo na osebnem računalniku naredili nov vsebnik, ki bo vključeval programa mandelbrot-seq.py in mandelbrot-par.py. Prvi izkorišča samo eno procesorsko jedro, drugi pa vsa razpoložljiva jedra. Izhajali bomo iz vsebnika python-scilib.sif, ki smo ga pripravili v prejšnjem razdelku.

V mapo /exec v vsebniku skopirajmo oba programa in osnovno konfiguracijsko datoteko default.conf. Mapo /exec vključimo v spremenljivko PATH, v kateri so navedene mape, kjer operacijski sistem išče izvršljive datoteke. Vsem uporabnikom nastavimo še pravice za zaganjanje oziroma branje datotek. Vsebnik opremimo z navodili za uporabo.

mb-cpu.def

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
Bootstrap: localimage
From: ../04-python-scilib/python-scilib.sif

%setup

%files
    ./prg/mandelbrot-seq.py /exec/
    ./prg/mandelbrot-par.py /exec/
    ./prg/default.conf /exec/

%environment
    export LC_ALL=C    
    export PATH=/exec:${PATH}

%post
    export LC_ALL=C    
    export DEBIAN_FRONTEND=noninteractive

    export PATH=/exec:${PATH}

    chmod a+rx /exec/*.py
    chmod a+r /exec/*.conf

%runscript
    echo "Usage: "
    echo "  apptainer exec mb-cpu.sif mandelbrot-seq.py"
    echo "  apptainer exec mb-cpu.sif mandelbrot-par.py"
    echo "  apptainer exec mb-cpu.sif mandelbrot-seq.py --config <config file>"
    echo "  apptainer exec mb-cpu.sif mandelbrot-par.py --config <config file>"
    echo
    echo "  <config file> example:"
    echo "--------------------------"
    cat /exec/default.conf
    echo "--------------------------"

%test
    echo "Mandelbrot set python scripts and config file:" 
    ls -lr /exec/*

%labels
    Author      uros (dot) lotric (at) fri (dot) uni (dash) lj (dot) si
    Container   Mandelbrot set on CPU
    Version     1.0
    Description Workshop advanced supercomputing (Superračunalištvo bolj zares)

%help
    For details run:
        ./mb-cpu.sif
Izvorna datoteka: mb-cpu.def

Vsebnik zaženimo na računskem vozlišču

Superračunalniki z odprtim dostopom pod okriljem SLING za lokalno upravljanje z viri uporabljajo vmesno programsko opremo Slurm.

Vsebnik zaženimo z ukazom srun. Zagnali bomo en posel, ki se bo izvajal na enem procesorskem jedru.

$ srun --ntasks=1 apptainer exec mb-cpu.sif mandelbrot-seq.py
SEQ: size: (3840, 2160) iterations: 256 time: 5.257 s
srun --ntasks=1 apptainer exec mb-cpu.sif mandelbrot-seq.py

Uporabimo več jeder

Zdaj izkoristimo več jeder. Zaženimo program mandelbrot-par.py, ki se od osnovnega razlikuje samo v vrstici 23, kjer prevajalniku z zahtevo parallel = True dovolimo paralelizacijo funkcije mandelbrot_colors.

Koda programa mandelbrot-par.py

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
#!/usr/bin/env python3


####################################################################################
#                                                                                  #
#  multi-core implementation of the Mandelbrot set                                 #
#                                                                                  #
####################################################################################


# import required libraries
import time, os
import argparse, configparser
import numpy as np
from PIL import Image
from numba import jit, prange


# mandelbrot_colors computes pixel colors in the image
#   real_min, real_max, imag_min, imag_max: bounds of the complex plane
#   iters_max: the maximum number of iterations
#   image: a reference to a memory location of the image
@jit(nopython = True, parallel = True, cache = True)
def mandelbrot_colors(real_min, real_max, imag_min, imag_max, iters_max, image):

    # image size in pixels
    width = image.shape[1]
    height = image.shape[0]

    # pixel size in complex plane
    real_step = (real_max - real_min) / width
    imag_step = (imag_max - imag_min) / height

    # check convergence of each pixel in the image
    for y in prange(0, height):
        for x in prange(0, width):              

            # a point in a complex plane corrsponding to the pixel (x, y)
            real = real_min + real_step * x
            imag = imag_min + imag_step * y
            c = complex(real, imag)

            # check for convergence
            z = complex(0, 0)
            iters = 0
            while abs(z) <= 2 and iters < iters_max:
                z = z*z + c
                iters += 1

            # color pixel in HSV scheme
            image[y, x] = (iters % 256, 255, 255 * (iters < iters_max))

# end mandelbrot_colors


# mandelbrot creates an image array of the Mandelbrot set
#   real_min, real_max, imag_min, imag_max: bounds of the complex plane
#   iters_max: the maximum number of iterations
#   width, height: size of the final image
def mandelbrot(real_min, real_max, imag_min, imag_max, iters_max, width, height):

    # allocate image array
    image = np.zeros((height, width, 3), dtype = np.uint8)

    # process image
    mandelbrot_colors(real_min, real_max, imag_min, imag_max, iters_max, image)

    # return image array
    return image

# end mandelbrot


# main routine
def main():

    # parse arguments
    ap = argparse.ArgumentParser()
    ap.add_argument('--config', type = str, default = '', help = 'config file')
    args = vars(ap.parse_args())
    config_file = args['config']

    # parse config file
    config = configparser.ConfigParser()
    if os.path.isfile(config_file):
        config.read(config_file)
    real_min = config.getfloat('AREA', 'real_min', fallback = -2.5)
    real_max = config.getfloat('AREA', 'real_max', fallback = +1.5)
    imag_min = config.getfloat('AREA', 'imag_min', fallback = -1.125)
    imag_max = config.getfloat('AREA', 'imag_max', fallback = +1.125)
    iters_max = config.getint('ITERATIONS', 'max', fallback = 256)
    width = config.getint('IMAGE', 'width', fallback = 3840)
    height = config.getint('IMAGE', 'height', fallback = 2160)
    name = config.get('IMAGE', 'name', fallback = 'mandelbrot.jpg')

    # main processing
    t = time.time()
    image = mandelbrot(real_min, real_max, imag_min, imag_max, iters_max, width, height)
    t = time.time() - t

    # save image
    Image.fromarray(image, mode='HSV').convert('RGB').save(name)    

    # printout
    print('PAR: size:', (width, height), 'iterations:', iters_max, 'time:', round(t, 3), "s")    

# end main


# invoke the main routine, when this is the main script
if __name__ == "__main__":
   main()
Izvorna datoteka: mandelbrot-par.py

Še enkrat zaženimo vsebnik, tokrat za posel zahtevamo 16 procesorsksih jedr.

$ srun --ntasks=1 --cpus-per-task=16 apptainer exec mb-cpu.sif mandelbrot-par.py
PAR: size: (3840, 2160) iterations: 256 time: 2.27 s
srun --ntasks=1 --cpus-per-task=16 apptainer exec mb-cpu.sif mandelbrot-par.py

Z majhno spremembo v programu smo izvajanje izdatno pohitrili.

Preveri svoje znanje

Vaja

Za pripravo recepta in gradnjo vsebnika sledimo navodilom: Vaja 05.