HTML

Programozó Páternoszter

Ez a Programozó Páternoszter (PP) blogja, a programozásról szól. Aktualitása, hogy a Debreceni Egyetem Informatikai Kara Magasszintű programozási nyelvek 1-2, C++ esattanulmányok, Java esettanulmányok című kurzusainak blogja is egyben.

A vadászat

A Debreceni Egyetem Programozói Évkönyve: az UDPROG projekt. A szakmai fórumunk a Facebook-en. Az új előadások a prezin.
A régi előadások:
Prog1:
1. C bevezetés
2. C tárgyalás
3. C befejezés
4. C a gyakorlatban
5. C++ bevezetés
6. C++ tárgyalás
7. C++ befejezés
8. C++ a gyakorlatban
9. Java platform bevezetés
10. Kvantuminformatikai alg. bev.
Prog2:
1. Java bevezetés
2. Java tárgyalás
3. Java befejezés
4. Java a gyakorlatban
5. Software Engineering bev.
6. Java EE bevezetés
7. AspectJ bevezetés
8. BPMN-BPEL SOA programozás
9. C++ haladó
10. Tensorflow

Kövess engem!

Friss topikok

Linkblog

A párhuzamosság gyönyörködtet

2011.03.27. 12:04 nb

Továbbra is az állatorvosi Mandelbrot halmazt számoljuk, de most nem a Qt-s ösvényen, hanem az előző poszt parancssorában járunk. Az ott megismert mandelpng.c++ programmal, aztán a P-szálakkal számoló pmandelpng.c++, végül az OpenMP-t használó ompmandelpng.c++ forrásokkal dolgozunk. Fordítsuk, futtassuk az elsőt, s közben figyeljük folyamatosan a

top -H -u norbi

parancs kimenetét, hogy lássunk is valamit, a forrásban felnyomtuk az iterációs határt: iteraciosHatar = 32000;

látszik, miközben dolgozik a program:

 

 Olyan fél perc kellett tehát a manőverhez. A PP alapján készítsünk egy olyan kódot, ami párhuzamosan tud dolgozni! A feladat erre alkalmas, hiszen az egyik szál számolja monjuk a kép első 300 sorát, egy másik szál pedig a 300-600-ig sorokat. Persze a programot általánosabban írjuk meg: egy szál a kép magassága / SZALAK_SZAMA darab sort számol majd ki. Ezt a megoldást is teszteljük!

Rend van a világban

Valóban: láthatóan fut a két szálunk:

 és a végrehajtási idő is a felére csökkent:

Azért a jól felkészült mérnök informatikus hallgató észrevesz némi zavart az erőben:

  • miért van külön PID-je a két P-szálnak, hova így az elmélet: a processzen belüli párhuzamosság, hogy azért választottuk ezt, hogy lássák a szálak a globális képet (amivel ugye nincs is baj, hiszen a kép rendben elkészül)
  • a másik a PP 64, PP 66-os mérések kb. ugyanazt mérik mindkét esetben, de a valós idő dupla annyi...

 ezekre varrjunk gombot egy-egy trófeáért persze!

OpenMP

Nem cifrázzuk: a legegyszerűbb megoldás is alkalmas, hiszen a feladatot triviálisan tudtuk párhuzamos részekre bontani. A részeket összefogó ciklus előtt jelezzük, hogy párhuzamosan akarjuk nyomni őket :

 

Ez a megoldás az összes lehetséges szálon párhuzamosítani fog, ez a mi esetünkben 4 magot és max. 4 szálat jelent, amit innen tudunk:

    std::cout << omp_get_num_procs () << std::endl;
    std::cout << omp_get_max_threads () << std::endl;
(A fordításnál a -O3 optimalizációs szintet se felejtsük el bekapcsolni, lásd majd a következő utáni képen.)

Lássuk a medvé(ket)

Szépen látszik, hogy dolgozik a 4 párhuzamos szál, adósok vagyunk még a fordítási, futtatási képpel, íme

Sokkal jobb, kb. 8 szekundumra faragtuk! Persze a -O3 kellett, s az első kettőt e nélkül toltuk. Azért tettünk így, hogy a kétszeres szorzó szépen látszódjon az első két esetben. De megmarad a rend a világban, ha mindhármat az O3 szintű optimalizációval fordítjuk. Ezzel az alábbi eredményeket toltuk:

g++ mandelpngt.c++ `libpng-config --ldflags` -O3 -o mandelpngt
time ./mandelpngt
real    0m18.691s

g++ pmandelpngt.c++ `libpng-config --ldflags` -lpthread -O3 -o pmandelpngt
time ./pmandelpngt b.png1. szal szamitas indul0. szal szamitas
real    0m9.495s

g++ ompmandelpngt.c++ `libpng-config --ldflags` -fopenmp -O3 -o ompmandelpngt
time ./ompmandelpngt c.png4
real    0m8.553s

Kattints a tovább linkre majd a forrásokhoz:

mandelpngt.c++

 

// Mandelbrot png
// Programozó Páternoszter
//
// Bátfai Norbert, nbatfai@inf.unideb.hu, nbatfai@gmail.com
// http://progpater.blog.hu/2011/03/26/kepes_egypercesek
//
// Fordítás:
// g++ mandelpng.c++ `libpng-config --ldflags` -o mandelpng

#include <iostream>
#include "png++/png.hpp"
#include <sys/times.h>

int main (int argc, char *argv[])
{
    // Mérünk időt (PP 64)
    clock_t delta = clock();
    // Mérünk időt (PP 66)
    struct tms tmsbuf1, tmsbuf2;
    times(&tmsbuf1);

    if (argc != 2) {
        std::cout << "Hasznalat: ./mandelpng fajlnev";
        return -1;
    }

    // számítás adatai
    double a = -2.0, b = .7,  c = -1.35, d = 1.35;
    int szelesseg = 600, magassag = 600, iteraciosHatar = 32000;

    // png-t készítünk a png++ csomaggal
    png::image <png::rgb_pixel> kep (szelesseg, magassag);

    // a számítás
    double dx = (b-a)/szelesseg;
    double dy = (d-c)/magassag;
    double reC, imC, reZ, imZ, ujreZ, ujimZ;
    // Hány iterációt csináltunk?
    int iteracio = 0;
    std::cout << "Szamitas";
    // Végigzongorázzuk a szélesség x magasság rácsot:
    for (int j=0; j<magassag; ++j) {
        //sor = j;
        for (int k=0; k<szelesseg; ++k) {
            // c = (reC, imC) a rács csomópontjainak
            // megfelelő komplex szám
            reC = a+k*dx;
            imC = d-j*dy;
            // z_0 = 0 = (reZ, imZ)
            reZ = 0;
            imZ = 0;
            iteracio = 0;
            // z_{n+1} = z_n * z_n + c iterációk
            // számítása, amíg |z_n| < 2 vagy még
            // nem értük el a 255 iterációt, ha
            // viszont elértük, akkor úgy vesszük,
            // hogy a kiinduláci c komplex számra
            // az iteráció konvergens, azaz a c a
            // Mandelbrot halmaz eleme
            while (reZ*reZ + imZ*imZ < 4 && iteracio < iteraciosHatar) {
                // z_{n+1} = z_n * z_n + c
                ujreZ = reZ*reZ - imZ*imZ + reC;
                ujimZ = 2*reZ*imZ + imC;
                reZ = ujreZ;
                imZ = ujimZ;

                ++iteracio;

            }

            kep.set_pixel(k, j, png::rgb_pixel(255-(255*iteracio)/iteraciosHatar,
                                               255-(255*iteracio)/iteraciosHatar,
                                               255-(255*iteracio)/iteraciosHatar));
        }
        std::cout << "." << std::flush;
    }

    kep.write (argv[1]);
    std::cout << argv[1] << " mentve" << std::endl;

    times(&tmsbuf2);
    std::cout << tmsbuf2.tms_utime-tmsbuf1.tms_utime
              + tmsbuf2.tms_stime-tmsbuf1.tms_stime << std::endl;

    delta = clock() - delta;
    std::cout << (double)delta/CLOCKS_PER_SEC << " sec" << std::endl;
}

 

pmandelpngt.c++

 

// Mandelbrot png párhuzamosan P-szálakkal
// Programozó Páternoszter
//
// Bátfai Norbert, nbatfai@inf.unideb.hu, nbatfai@gmail.com
// http://progpater.blog.hu/2011/03/26/kepes_egypercesek
//
// Fordítás:
// g++ pmandelpng.c++ `libpng-config --ldflags` -lpthread -o pmandelpng

#include <iostream>
#include "png++/png.hpp"
#include <pthread.h>
#include <time.h>
#include <sys/times.h>

#define SZALAK_SZAMA 4

// számítás adatai
double a = -2.0, b = .7,  c = -1.35, d = 1.35;
int szelesseg = 600, magassag = 600, iteraciosHatar = 32000;

// png-t készítünk a png++ csomaggal
png::image <png::rgb_pixel> kep (szelesseg, magassag);


void *mandel_resz_szamitas(void *id)
{
    int mettol = *(int *)id * ( magassag / SZALAK_SZAMA);
    int meddig = mettol + (magassag/SZALAK_SZAMA);

    // a számítás
    double dx = (b-a)/szelesseg;
    double dy = (d-c)/magassag;
    double reC, imC, reZ, imZ, ujreZ, ujimZ;
    // Hány iterációt csináltunk?
    int iteracio = 0;
    std::cout << *(int *)id << ". szal szamitas indul";
    // Végigzongorázzuk a szélesség x magasság rácsot:
    for (int j=mettol; j<meddig; ++j) {
        //sor = j;
        for (int k=0; k<szelesseg; ++k) {
            // c = (reC, imC) a rács csomópontjainak
            // megfelelő komplex szám
            reC = a+k*dx;
            imC = d-j*dy;
            // z_0 = 0 = (reZ, imZ)
            reZ = 0;
            imZ = 0;
            iteracio = 0;
            // z_{n+1} = z_n * z_n + c iterációk
            // számítása, amíg |z_n| < 2 vagy még
            // nem értük el a 255 iterációt, ha
            // viszont elértük, akkor úgy vesszük,
            // hogy a kiinduláci c komplex számra
            // az iteráció konvergens, azaz a c a
            // Mandelbrot halmaz eleme
            while (reZ*reZ + imZ*imZ < 4 && iteracio < iteraciosHatar) {
                // z_{n+1} = z_n * z_n + c
                ujreZ = reZ*reZ - imZ*imZ + reC;
                ujimZ = 2*reZ*imZ + imC;
                reZ = ujreZ;
                imZ = ujimZ;

                ++iteracio;

            }

            kep.set_pixel(k, j, png::rgb_pixel(255-(255*iteracio)/iteraciosHatar,
                                               255-(255*iteracio)/iteraciosHatar,
                                               255-(255*iteracio)/iteraciosHatar));
        }
        std::cout << "." << std::flush;
    }

    return id;
}

int main (int argc, char *argv[])
{
    // Mérünk időt (PP 64)
    clock_t delta = clock();
    // Mérünk időt (PP 66)
    struct tms tmsbuf1, tmsbuf2;
    times(&tmsbuf1);

    if (argc != 2) {
        std::cout << "Hasznalat: ./mandelpng fajlnev";
        return -1;
    }

    pthread_t szal[SZALAK_SZAMA];
    int szal_arg[SZALAK_SZAMA];
    int i, *r;
    // SZALAK_SZAMA db P-szálat készítünk
    for (i=0; i<SZALAK_SZAMA; ++i) {
        szal_arg[i] = i;
        // a párhuzamosan végrehajtandó kód a
        // mandel_resz_szamitas függvényben
        if (pthread_create(szal+i, NULL,
                           mandel_resz_szamitas, (void *)(szal_arg+i)))
            exit(-1);
    }
    // Megvárjuk, hogy minden szál befejezze a melót
    for (i=0; i<SZALAK_SZAMA; ++i) {
        pthread_join(szal[i], (void **) &r);
        std::cout << *r << ". szal kesz.";
    }
    // Ha kész vannak a szálak, kinyomjuk fájlba a képet
    kep.write (argv[1]);
    std::cout << argv[1] << " mentve" << std::endl;

    times(&tmsbuf2);
    std::cout << tmsbuf2.tms_utime-tmsbuf1.tms_utime
              + tmsbuf2.tms_stime-tmsbuf1.tms_stime << std::endl;

    delta = clock() - delta;
    std::cout << (double)delta/CLOCKS_PER_SEC << " sec" << std::endl;

}

 

ompmandelpngt.c++

 

// Mandelbrot png
// Programozó Páternoszter
//
// Bátfai Norbert, nbatfai@inf.unideb.hu, nbatfai@gmail.com
// http://progpater.blog.hu/2011/03/26/kepes_egypercesek
//
// Fordítás:
// g++ ompmandelpngt.c++ `libpng-config --ldflags` -fopenmp -O3 -o ompmandelpngt

#include <iostream>
#include "png++/png.hpp"
#include <sys/times.h>
#include <omp.h>

int main (int argc, char *argv[])
{
    // Mérünk időt (PP 64)
    clock_t delta = clock();
    // Mérünk időt (PP 66)
    struct tms tmsbuf1, tmsbuf2;
    times(&tmsbuf1);

    std::cout << omp_get_num_procs () << std::endl; 
    std::cout << omp_get_max_threads () << std::endl; 
    
    if (argc != 2) {
        std::cout << "Hasznalat: ./mandelpng fajlnev";
        return -1;
    }

    // számítás adatai
    double a = -2.0, b = .7,  c = -1.35, d = 1.35;
    int szelesseg = 600, magassag = 600, iteraciosHatar = 32000;

    // png-t készítünk a png++ csomaggal
    png::image <png::rgb_pixel> kep (szelesseg, magassag);

    // a számítás
    double dx = (b-a)/szelesseg;
    double dy = (d-c)/magassag;
    double reC, imC, reZ, imZ, ujreZ, ujimZ;
    // Hány iterációt csináltunk?
    int iteracio = 0;
    std::cout << "Szamitas";
    // Végigzongorázzuk a szélesség x magasság rácsot:
    #pragma omp parallel for
    for (int j=0; j<magassag; ++j) {
        //sor = j;
        for (int k=0; k<szelesseg; ++k) {
            // c = (reC, imC) a rács csomópontjainak
            // megfelelő komplex szám
            reC = a+k*dx;
            imC = d-j*dy;
            // z_0 = 0 = (reZ, imZ)
            reZ = 0;
            imZ = 0;
            iteracio = 0;
            // z_{n+1} = z_n * z_n + c iterációk
            // számítása, amíg |z_n| < 2 vagy még
            // nem értük el a 255 iterációt, ha
            // viszont elértük, akkor úgy vesszük,
            // hogy a kiinduláci c komplex számra
            // az iteráció konvergens, azaz a c a
            // Mandelbrot halmaz eleme
            while (reZ*reZ + imZ*imZ < 4 && iteracio < iteraciosHatar) {
                // z_{n+1} = z_n * z_n + c
                ujreZ = reZ*reZ - imZ*imZ + reC;
                ujimZ = 2*reZ*imZ + imC;
                reZ = ujreZ;
                imZ = ujimZ;

                ++iteracio;

            }

            kep.set_pixel(k, j, png::rgb_pixel(255-(255*iteracio)/iteraciosHatar,
                                               255-(255*iteracio)/iteraciosHatar,
                                               255-(255*iteracio)/iteraciosHatar));
                                               
        }
        std::cout << "." << std::flush;
    }

    kep.write (argv[1]);
    std::cout << argv[1] << " mentve" << std::endl;

    times(&tmsbuf2);
    std::cout << tmsbuf2.tms_utime-tmsbuf1.tms_utime
              + tmsbuf2.tms_stime-tmsbuf1.tms_stime << std::endl;

    delta = clock() - delta;
    std::cout << (double)delta/CLOCKS_PER_SEC << " sec" << std::endl;
}

 

Szólj hozzá!

Címkék: top mandelbrot párhuzamosság pthread openmp p szálak párhuzamos programozás konkurrens programozás

A bejegyzés trackback címe:

https://progpater.blog.hu/api/trackback/id/tr422775108

Kommentek:

A hozzászólások a vonatkozó jogszabályok  értelmében felhasználói tartalomnak minősülnek, értük a szolgáltatás technikai  üzemeltetője semmilyen felelősséget nem vállal, azokat nem ellenőrzi. Kifogás esetén forduljon a blog szerkesztőjéhez. Részletek a  Felhasználási feltételekben és az adatvédelmi tájékoztatóban.

Nincsenek hozzászólások.
süti beállítások módosítása