Creare un codice indipendente ed immutabile per far lavorare in multithreading un ciclo for parallelizzabile

di il
47 risposte

47 Risposte - Pagina 3

  • Re: Creare un codice indipendente ed immutabile per far lavorare in multithreading un ciclo for parallelizzabile

    Ho letto la guida che hai postato ma ci sono 2 problemi di fondo:
    1) Non posso calcolare il numero di core automaticamente nei file .properties;
    2) In questo momento ho un i3-8100 ed un SSD Crucial M500 pertanto ottengo 5 come conteggio (4*1+1=5) ma ho anche provato con 9, 10 ed 1. Impiego sempre 36 secondi.
    Credo che mi debba accontentare, la scrittura di record sul database è lenta. Forse riesco a migliorare la mia applicazione cambiando DB e prendendo Oracle oppure MongoDB. Il primo però è adatto a chi scrive codice per il lavoro, non va bene per progetti privi di un risvolto economico mentre il secondo mi costringerebbe a modificare tutta la mia applicazione perché nella WebApp sfrutto le funzionalità relazionali del DB.

    L'unica cosa che potrei fare è usare MySQL, si tratterebbe di editare tutte le query nelle classi Repository, un lavorone ma non certo infinito. A tuo avviso con MySQL avrei un miglioramento oppure no? Su internet ho letto che MySQL è meno sicuro ma più rapido. Wordpress, Prestashop, Magento, Drupal, ecc... usano tutti MySQL, forse un motivo c'è.
  • Re: Creare un codice indipendente ed immutabile per far lavorare in multithreading un ciclo for parallelizzabile

    iBaffiPro ha scritto:


    1) Non posso calcolare il numero di core automaticamente nei file .properties;
    Ma che c'entra ... non devi mica fare una cosa del genere (che non ha senso, comunque).
    Tu SAI in quale/i ambiente/i, in termini di hw/sw, dovrà girare la tua applicazione. Configurerai quindi l'applicazione per ciascun specifico ambiente. Se il dubbio è: come/dove mettere valori di properties differenti per vari ambienti ... questa è un'altra questione ...

    iBaffiPro ha scritto:


    2) In questo momento ho un i3-8100 ed un SSD Crucial M500 pertanto ottengo 5 come conteggio (4*1+1=5) ma ho anche provato con 9, 10 ed 1. Impiego sempre 36 secondi.
    Ma dipende da COSA fai in quei 36 secondi. Se, per dire, fai una chiamata ad un controller e nella logica fai un banale ciclo for lineare in cui fai un tot di insert in sequenza (e tutto questo impiega 36 sec.)... allora non conta quanti core né quante connection hai a disposizione.

    iBaffiPro ha scritto:


    Forse riesco a migliorare la mia applicazione cambiando DB e prendendo Oracle oppure MongoDB.
    Ma scherzi? A parte il fatto dell'ambito certamente più "commerciale" di Oracle DB (pur potendo comunque usare la Express Edition), è un "carrozzone" enorme che non so nemmeno se la tua macchina lo regge. Se hai 8GB di RAM (o peggio di meno!) dimenticati pure il Oracle DB.

    iBaffiPro ha scritto:


    si tratterebbe di editare tutte le query nelle classi Repository, un lavorone ma non certo infinito.
    Guarda che se non hai usato cose davvero particolari (function o costrutti specifici) di PostgreSQL, il SQL delle query dovrebbe essere molto "portabile". Quello che invece cambierebbe di certo è la parte DDL, cioè le CREATE TABLE ecc... Lì sì, ovviamente può cambiare e anche molto.
  • Re: Creare un codice indipendente ed immutabile per far lavorare in multithreading un ciclo for parallelizzabile

    Grazie, ora valuto cosa fare
  • Re: Creare un codice indipendente ed immutabile per far lavorare in multithreading un ciclo for parallelizzabile

    Scusa se ritorno sulla questione ma volevo applicare il tuo codice su un metodo di un'altra classe che a mio avviso potrebbe beneficiare concretamente della parallelizzazione dei processi. Non ti posto la classe reale perché sarebbe troppo lunga. Ho creato un progetto che puoi caricare sul tuo Eclipse che spiega per bene quello che voglio fare. In sostanza ho una lista di liste che idealizza una matrice:
    matrix =
    2 3 7 9
    5 6 0 4
    … molte altre righe …
    0 2 4 3
    4 5 2 7
    Il mio obiettivo è prendere tutti i valori inferiori ad un certo numero (supponiamo il numero 3 escluso) e sostituirli con il valore 'null'. Inoltre, tutti i valori inferiore a questo limite li voglio inserire in un altro oggetto (una semplice lista). L'output di quello che vedi sopra sarebbe questo:
    new matrix =
    N 3 7 9
    5 6 N 4
    … molte altre righe …
    N N 4 3
    4 5 N 7
    removed number = [2 0 0 2 2]
    Ovviamente nel mio caso reale faccio altro ma il problema è identico (devo editare una lista ed aggiungere elementi in un'altra).
    ElementMutator > La classe che hai scritto tu;
    ForkJoinListMutator > La classe che hai scritto tu;
    ForkJoinListMutatorExample > La classe con il main() dove si avviano i 2 metodi;
    GenerateMatrix > La classe che produce la "matrice" con qualche colonna e molte righe.
    MultiThreadClass > La parallelizzazione di SingleThreadClass
    SingleThreadClass > La classe che sfrutta esegue le operazioni sfruttando 1 thread solo del proprio PC
    Queste classi non posso essere editate: GenerateMatrix, SingleThreadClass e ForkJoinListMutatorExample.
    Ho aggiunto reentrantReadWriteLock perché penso che il problema sia un problema di condivisione di risorse ma il catch() non mi fornisce alcuna informazione sulla causa.
    
    package metodo_java8_moderno;
    
    import java.util.ArrayList;
    import java.util.List;
    import java.util.Random;
    
    public class GenerateMatrix {
    
        public List<List<Long>> generate(int columns, int rows){
            List<List<Long>> matrix = new ArrayList<>();
            List<Long> row;
            Random randomGenerator = new Random();
            for(int i=0; i<rows; i++){
                row = new ArrayList<>();
                for(int j=0; j<columns; j++){
                    row.add((long) randomGenerator.nextInt(columns*rows+1));
                }
                matrix.add(row);
            }
            return matrix;
        }
    }
    
    
    package metodo_java8_moderno;
    
    import java.util.List;
    
    public class ForkJoinListMutatorExample {
    
        public static void main(String args[]) {
            GenerateMatrix generateMatrix = new GenerateMatrix();
            List<List<Long>> matrix = generateMatrix.generate(3,4);
            System.out.println(matrix);
            long t1 = System.nanoTime();
            SingleThreadClass singleThreadClass = new SingleThreadClass();
            List<Object> output = singleThreadClass.editAndAdd(matrix);
            long t2 = System.nanoTime();
            System.out.println("Time taken single thread process: " + (t2-t1)/100000000);
            List<List<Long>> newMatrix = (List<List<Long>>) output.get(0);
            List<Long> numbers = (List<Long>) output.get(1);
            System.out.println(newMatrix);
            System.out.println(numbers);
            t1 = System.nanoTime();
            MultiThreadClass multiThreadClass = new MultiThreadClass();
            output = multiThreadClass.editAndAdd(matrix);
            t2 = System.nanoTime();
            System.out.println("Time taken multi thread process: " + (t2-t1)/100000000);
            List<List<Long>> newMatrix2 = (List<List<Long>>) output.get(0);
            List<Long> numbers2 = (List<Long>) output.get(1);
            System.out.println(newMatrix2);
            System.out.println(numbers2);
    
        }
    
    }
    
    
    package metodo_java8_moderno;
    
    import java.util.ArrayList;
    import java.util.List;
    import java.util.concurrent.locks.ReentrantReadWriteLock;
    
    public class MultiThreadClass {
    
    
        private ReentrantReadWriteLock reentrantReadWriteLock = new ReentrantReadWriteLock();
    
        public List<List<Long>> matrix;
        public void setMatrix(List<List<Long>> matrix) {
            reentrantReadWriteLock.writeLock().lock();
            try {
                this.matrix = matrix;
            } catch (Exception e){
                this.matrix = null;
            } finally {
                reentrantReadWriteLock.writeLock().unlock();
            }
        }
        public List<List<Long>> getMatrix() {
            reentrantReadWriteLock.readLock().lock();
            List<List<Long>> matrix;
            try {
                matrix = this.matrix;
            } catch (Exception e){
                matrix = null;
            } finally {
                reentrantReadWriteLock.readLock().unlock();
            }
            return matrix;
        }
    
        public int i;
        public void setI(int i) {
            reentrantReadWriteLock.writeLock().lock();
            try {
                this.i = i;
            } catch (Exception e){
                this.i = -1;
            } finally {
                reentrantReadWriteLock.writeLock().unlock();
            }
        }
        public int getI() {
            reentrantReadWriteLock.readLock().lock();
            int i;
            try {
                i = this.i;
            } catch (Exception e){
                i = -1;
            } finally {
                reentrantReadWriteLock.readLock().unlock();
            }
            return i;
        }
    
        public List<Long> numbers;
        public void setNumbers(List<Long> numbers) {
            reentrantReadWriteLock.writeLock().lock();
            try {
                this.numbers = numbers;
            } catch (Exception e){
                this.numbers = null;
            } finally {
                reentrantReadWriteLock.writeLock().unlock();
            }
        }
        public List<Long> getNumbers() {
            reentrantReadWriteLock.readLock().lock();
            List<Long> numbers;
            try {
                numbers = this.numbers;
            } catch (Exception e){
                numbers = null;
            } finally {
                reentrantReadWriteLock.readLock().unlock();
            }
            return numbers;
        }
    
        public Long limit;
        public void setLimit(Long limit) {
            reentrantReadWriteLock.writeLock().lock();
            try {
                this.limit = limit;
            } catch (Exception e){
                this.limit = null;
            } finally {
                reentrantReadWriteLock.writeLock().unlock();
            }
        }
        public Long getLimit() {
            reentrantReadWriteLock.readLock().lock();
            Long limit;
            try {
                limit = this.limit;
            } catch (Exception e){
                limit = null;
            } finally {
                reentrantReadWriteLock.readLock().unlock();
            }
            return limit;
        }
    
        public List<Object> editAndAdd(List<List<Long>> matrix){
            this.matrix = matrix;
            this.limit = (long) this.matrix.get(0).size()*this.matrix.size()/2;
            this.numbers = new ArrayList<>();
            int core = Runtime.getRuntime().availableProcessors();
            for(int i=0; i<this.matrix.get(0).size(); i++){
                this.i = i;
                ForkJoinListMutator listMutator = ForkJoinListMutator.getDefault();
                listMutator.mutate(this.matrix,Math.max(1,this.matrix.size()/(core*1+1)),(j) -> parallelFor(j));
            }
            List<Object> objectList = new ArrayList<>();
            objectList.add(this.matrix);
            objectList.add(this.numbers);
            return objectList;
        }
    
    
        public void parallelFor(int j){
            try{
                List<List<Long>> matrix = getMatrix();
                int i = getI();
                List<Long> numbers = getNumbers();
                Long limit = getLimit();
                if(matrix.get(j).get(i).longValue() <= limit){
                    numbers.add(matrix.get(j).get(i));
                    matrix.get(j).set(i,null);
                }
                setMatrix(matrix);
                setI(i);
                setNumbers(numbers);
                setLimit(limit);
                //System.out.println(">> "+this.matrix);
                //System.out.println(">> "+this.numbers);
            }catch (Exception e){
                System.out.println("Errore!");
                System.out.println(e.getMessage());
                System.out.println(e.getCause());
            }
    
        }
    
    }
    
    
    
    package metodo_java8_moderno;
    
    @FunctionalInterface
    public interface ElementMutator<T> {
        void apply(int i);
    }
    
    
    
    package metodo_java8_moderno;
    
    import java.util.List;
    import java.util.concurrent.ForkJoinPool;
    import java.util.concurrent.RecursiveAction;
    
    public class ForkJoinListMutator {
    
        public static final int DEFAULT_SEQ_THRESHOLD = 10000;
    
        private static final ForkJoinListMutator defaultInstance = new ForkJoinListMutator(ForkJoinPool.commonPool());
    
        private final ForkJoinPool forkJoinPool;
    
        public ForkJoinListMutator(ForkJoinPool forkJoinPool) {
            this.forkJoinPool = forkJoinPool;
        }
    
        public static ForkJoinListMutator getDefault() {
            return defaultInstance;
        }
    
        public <T> void mutate(List<T> list, ElementMutator<T> mutator) {
            mutate(list, DEFAULT_SEQ_THRESHOLD, mutator);
        }
    
        public <T> void mutate(List<T> list, int seqThreshold, ElementMutator<T> mutator) {
            MutateTask<T> mainTask = new MutateTask<>(list, seqThreshold, mutator);
            forkJoinPool.invoke(mainTask);
        }
    
        private static class MutateTask<T> extends RecursiveAction {
    
            private final List<T> list;
            private final int start;
            private final int end;
            private final int seqThreshold;
            private final ElementMutator<T> mutator;
    
            public MutateTask(List<T> list, int seqThreshold, ElementMutator<T> mutator) {
                this(list, 0, list.size(), seqThreshold, mutator);
            }
    
            public MutateTask(List<T> list, int start, int end, int seqThreshold, ElementMutator<T> mutator) {
                this.list = list;
                this.start = start;
                this.end = end;
                this.seqThreshold = seqThreshold;
                this.mutator = mutator;
            }
    
            @Override
            protected void compute() {
    
                final int length = end - start;
    
                if (length <= seqThreshold) {
                    computeSequentially();
                } else {
                    MutateTask<T> leftTask = new MutateTask<>(list, start, start+length/2, seqThreshold, mutator);
                    leftTask.fork();
                    leftTask.join();
                    MutateTask<T> rightTask = new MutateTask<>(list, start+length/2, end, seqThreshold, mutator);
                    rightTask.compute();
                }
            }
    
            private void computeSequentially() {
                for (int i = start; i < end; i++) {
                    mutator.apply(i);
                }
            }
    
        }
    }
    
    
    
    package metodo_java8_moderno;
    
    import java.util.ArrayList;
    import java.util.List;
    
    public class SingleThreadClass {
    
        public List<Object> editAndAdd(List<List<Long>> matrix){
            Long limit = (long) matrix.get(0).size()*matrix.size()/2;
            List<Long> numbers = new ArrayList<>();
            for(int i=0; i<matrix.get(0).size(); i++){
                for(int j=0; j<matrix.size(); j++){
                    if(matrix.get(j).get(i).longValue() <= limit){
                        numbers.add(matrix.get(j).get(i));
                        matrix.get(j).set(i,null);
                    }
                }
            }
            List<Object> objectList = new ArrayList<>();
            objectList.add(matrix);
            objectList.add(numbers);
            return objectList;
        }
    
    }
    
    
  • Re: Creare un codice indipendente ed immutabile per far lavorare in multithreading un ciclo for parallelizzabile

    iBaffiPro ha scritto:


    In sostanza ho una lista di liste che idealizza una matrice:
    matrix =
    2 3 7 9
    5 6 0 4
    … molte altre righe …
    0 2 4 3
    4 5 2 7
    Il mio obiettivo è prendere tutti i valori inferiori ad un certo numero (supponiamo il numero 3 escluso) e sostituirli con il valore 'null'. Inoltre, tutti i valori inferiore a questo limite li voglio inserire in un altro oggetto (una semplice lista).
    Non ho purtroppo molto tempo/modo ora di vedere per bene quello che hai scritto, lo farò di certo nei prossimi giorni appena possibile. Ma una cosa mi è subito saltata all'occhio: hai usato un List<List<Long>>

    Ora, la questione è: se la tua matrice NON deve essere "espandibile" (cioè cambiare dimensione), allora un List<List<Long>> è un po' troppo, nel senso che "spreca" molto in termini di oggetti/memoria. Sarebbe meglio un semplice Long[][] (ovviamente un Long wrapper, non primitivo, dato che vuoi gestire i null).

    Ma la seconda questione è: indipendentemente da come intenderesti modellare la matrice (con List<List<Long>> o Long[][] ) perché non hai incapsulato questa gestione in una apposita classe es. Matrix (o Matrice in italiano)???
    La programmazione "ad oggetti" serve proprio anche per questo: nascondere i dettagli di come sono fatti e come funzionano gli oggetti al loro interno.

    Invece ti porti dietro ovunque un List<List<Long>> che è noioso da scrivere, difficile da gestire, difficile da rendere eventualmente thread-safe, ecc...

    Poi ultima questione: il multi-threading/parallelizzazione a cosa ti servirebbe in questo caso specifico delle matrici? Quante matrici hai? Se ne avessi 10000 di matrici su cui devi operare, allora può aver senso. Ma se ne hai poche no.
  • Re: Creare un codice indipendente ed immutabile per far lavorare in multithreading un ciclo for parallelizzabile

    Se con Long[][] posso risparmiare memoria allora forse non ho fatto la scelta migliore. List<List<Long>> ha però dei vantaggi che Long[][] non ha, editAndAdd() è solo uno dei tanti metodi di SingleThreadClass. Preferisco un List<List<Long>> oppure un Long[][] e non un'oggetto specifico perché il programma che leggi non è quello del caso reale. Ci sono anche cicli for che scorrono sulle colonne. Inoltre il numero di colonne non è sempre costante. Io ho solo una matrice ma questo codice qui:
    
                for(int j=0; j<matrix.size(); j++){
                    if(matrix.get(j).get(i).longValue() <= limit){
                        numbers.add(matrix.get(j).get(i));
                        matrix.get(j).set(i,null);
                    }
                }
    
    ovvero il contenuto del 2° ciclo for non è quello del mio caso reale. Detto in altro modo non mi aspetto che MultiThreadClass di questo programmino sia più veloce di SingleThreadClass, anzi mi aspetto che sia il contrario. Nel mio caso reale invece mi aspetto un 30% oppure anche un 50% di miglioramento dei tempi di calcolo sul mio i3 con 4 core.
  • Re: Creare un codice indipendente ed immutabile per far lavorare in multithreading un ciclo for parallelizzabile

    iBaffiPro ha scritto:


    perché il programma che leggi non è quello del caso reale.

    ovvero il contenuto del 2° ciclo for non è quello del mio caso reale.
    Ovviamente io NON posso sapere quale è il tuo caso reale. Da una prima occhiata al codice (non ho verificato né provato ancora nulla) comunque posso dirti che è un pessimo codice, specialmente quella tua classe MultiThreadClass che è un "obbrobrio" quasi totale.
    E inoltre le prove che fai in quel main valgono poco, perché te lo avevo già detto: con una singola e unica prova così, ottieni delle tempistiche che sono assolutamente INAFFIDABILI e fuorvianti.

    Quindi non posso fare altro (per ora) che ribadirti quanto avevo già detto: completa il prima possibile questa/e esercitazioni per concentrarti su un buon libro come quello che avevo citato e che continuo a suggerire ad altri sul forum.
  • Re: Creare un codice indipendente ed immutabile per far lavorare in multithreading un ciclo for parallelizzabile

    Si lo farò sicuramente, tanto ormai manca poco, spero di poter leggere 2 libri nell'estate, il primo su Java ed il secondo sulle basi di dati. Aprirò una discussione anche su questo perché ci sono delle cose che ti voglio chiedere. Purtroppo, la chiusura di questa esercitazione richiede più tempo del previsto. MultiThreadClass è effettivamente un obbrobrio ma c'è un altro aspetto che mi preoccupa maggiormente che è l’aggiunta di variabili di classe. Quello che non mi posso permettere, anche perché i metodi sono molti ed il codice lungo e complesso, è di modificare questo pezzetto:
    
                if(matrix.get(j).get(i).longValue() <= limit){
                    numbers.add(matrix.get(j).get(i));
                    matrix.get(j).set(i,null);
                }
    
    che nella realtà è diverso, lunghissimo e consuma molti cicli di CPU.

    Si si, lo so che le prove del main() valgono poco, le ho scritte solo per verificare che MultiThreadClass e SingleThreadClass funzionino, non guardarle neppure, anzi cancellale se preferisci. Ho scritto la classe per creare uno script che una volta caricato su un IDE funzionasse.
  • Re: Creare un codice indipendente ed immutabile per far lavorare in multithreading un ciclo for parallelizzabile

    Se metto tutto in una classe esterna, cosa che comunque non mi piace perché il codice diventa troppo verboso, non risolvo. Se aggiungo un addNumebers() non risolvo neppure. addNumebers() è inaccettabile perché l'obiettivo è tenere assolutamente questo pezzo invariato:
    
                    if(matrix.get(j).get(i).longValue() <= limit){
                        numbers.add(matrix.get(j).get(i));
                        matrix.get(j).set(i,null);
                    }
    
    Il problema è numbers, non matrix. matrix viene editata correttamente.
    Approccio 1
    
    package metodo_java8_moderno;
    
    import java.util.List;
    import java.util.concurrent.locks.ReentrantReadWriteLock;
    
    public class SynchronizedObjects {
    
        public List<List<Long>> matrix;
        public Long limit;
        public int i;
        public List<Long> numbers;
    
        public SynchronizedObjects(List<List<Long>> matrix, Long limit, int i, List<Long> numbers) {
            this.matrix = matrix;
            this.limit = limit;
            this.i = i;
            this.numbers = numbers;
        }
    
        private ReentrantReadWriteLock reentrantReadWriteLock = new ReentrantReadWriteLock();
    
        public void setMatrix(List<List<Long>> matrix) {
            reentrantReadWriteLock.writeLock().lock();
            try {
                this.matrix = matrix;
            } catch (Exception e){
                this.matrix = null;
            } finally {
                reentrantReadWriteLock.writeLock().unlock();
            }
        }
    
        public List<List<Long>> getMatrix() {
            reentrantReadWriteLock.readLock().lock();
            List<List<Long>> matrix;
            try {
                matrix = this.matrix;
            } catch (Exception e){
                matrix = null;
            } finally {
                reentrantReadWriteLock.readLock().unlock();
            }
            return matrix;
        }
    
        public void setI(int i) {
            reentrantReadWriteLock.writeLock().lock();
            try {
                this.i = i;
            } catch (Exception e){
                this.i = -1;
            } finally {
                reentrantReadWriteLock.writeLock().unlock();
            }
        }
    
        public int getI() {
            reentrantReadWriteLock.readLock().lock();
            int i;
            try {
                i = this.i;
            } catch (Exception e){
                i = -1;
            } finally {
                reentrantReadWriteLock.readLock().unlock();
            }
            return i;
        }
    
        public void setNumbers(List<Long> numbers) {
            reentrantReadWriteLock.writeLock().lock();
            try {
                this.numbers = numbers;
            } catch (Exception e){
                this.numbers = null;
            } finally {
                reentrantReadWriteLock.writeLock().unlock();
            }
        }
    
        public List<Long> getNumbers() {
            reentrantReadWriteLock.readLock().lock();
            List<Long> numbers;
            try {
                numbers = this.numbers;
            } catch (Exception e){
                numbers = null;
            } finally {
                reentrantReadWriteLock.readLock().unlock();
            }
            return numbers;
        }
        
        public void setLimit(Long limit) {
            reentrantReadWriteLock.writeLock().lock();
            try {
                this.limit = limit;
            } catch (Exception e){
                this.limit = null;
            } finally {
                reentrantReadWriteLock.writeLock().unlock();
            }
        }
    
        public Long getLimit() {
            reentrantReadWriteLock.readLock().lock();
            Long limit;
            try {
                limit = this.limit;
            } catch (Exception e){
                limit = null;
            } finally {
                reentrantReadWriteLock.readLock().unlock();
            }
            return limit;
        }
    
    }
    
    
    package metodo_java8_moderno;
    
    import java.util.ArrayList;
    import java.util.List;
    
    public class MultiThreadClass {
    
        public SynchronizedObjects so;
    
        public List<Object> editAndAdd(List<List<Long>> matrix){
            this.so = new SynchronizedObjects(
                    matrix,
                    (long) matrix.get(0).size()*matrix.size()/2,
                    0,
                    new ArrayList<>()
            );
            int core = Runtime.getRuntime().availableProcessors();
            for(int i=0; i<matrix.get(0).size(); i++){
                so.setI(i);
                ForkJoinListMutator listMutator = ForkJoinListMutator.getDefault();
                listMutator.mutate(so.getMatrix(),Math.max(1,so.getMatrix().size()/(core*1+1)),(j) -> parallelFor(j));
            }
            List<Object> objectList = new ArrayList<>();
            objectList.add(so.getMatrix());
            objectList.add(so.getNumbers());
            return objectList;
        }
    
        public void parallelFor(int j){
            try{
                List<List<Long>> matrix = so.getMatrix();
                int i = so.getI();
                List<Long> numbers = so.getNumbers();
                Long limit = so.getLimit();
                if(matrix.get(j).get(i).longValue() <= limit){
                    numbers.add(matrix.get(j).get(i));
                    matrix.get(j).set(i,null);
                }
                so.setNumbers(numbers);
            }catch (Exception e){
                System.out.println("Errore!");
                System.out.println(e.getMessage());
                System.out.println(e.getCause());
            }
        }
    
    }
    
    Approccio 2
    
    package metodo_java8_moderno;
    
    import java.util.List;
    import java.util.concurrent.locks.ReentrantReadWriteLock;
    
    public class SynchronizedObjects {
    
        public List<List<Long>> matrix;
        public Long limit;
        public int i;
        public List<Long> numbers;
    
        public SynchronizedObjects(List<List<Long>> matrix, Long limit, int i, List<Long> numbers) {
            this.matrix = matrix;
            this.limit = limit;
            this.i = i;
            this.numbers = numbers;
        }
    
        private ReentrantReadWriteLock reentrantReadWriteLock = new ReentrantReadWriteLock();
    
        public void setMatrix(List<List<Long>> matrix) {
            reentrantReadWriteLock.writeLock().lock();
            try {
                this.matrix = matrix;
            } catch (Exception e){
                this.matrix = null;
            } finally {
                reentrantReadWriteLock.writeLock().unlock();
            }
        }
    
        public List<List<Long>> getMatrix() {
            reentrantReadWriteLock.readLock().lock();
            List<List<Long>> matrix;
            try {
                matrix = this.matrix;
            } catch (Exception e){
                matrix = null;
            } finally {
                reentrantReadWriteLock.readLock().unlock();
            }
            return matrix;
        }
    
        public void setI(int i) {
            reentrantReadWriteLock.writeLock().lock();
            try {
                this.i = i;
            } catch (Exception e){
                this.i = -1;
            } finally {
                reentrantReadWriteLock.writeLock().unlock();
            }
        }
    
        public int getI() {
            reentrantReadWriteLock.readLock().lock();
            int i;
            try {
                i = this.i;
            } catch (Exception e){
                i = -1;
            } finally {
                reentrantReadWriteLock.readLock().unlock();
            }
            return i;
        }
    
        public void setNumbers(List<Long> numbers) {
            reentrantReadWriteLock.writeLock().lock();
            try {
                this.numbers = numbers;
            } catch (Exception e){
                this.numbers = null;
            } finally {
                reentrantReadWriteLock.writeLock().unlock();
            }
        }
    
        public List<Long> getNumbers() {
            reentrantReadWriteLock.readLock().lock();
            List<Long> numbers;
            try {
                numbers = this.numbers;
            } catch (Exception e){
                numbers = null;
            } finally {
                reentrantReadWriteLock.readLock().unlock();
            }
            return numbers;
        }
    
        public void addNumbers(Long number) {
            reentrantReadWriteLock.readLock().lock();
            try {
                numbers.add(number);
            } catch (Exception e){
                //
            } finally {
                reentrantReadWriteLock.readLock().unlock();
            }
            return;
        }
    
        public void setLimit(Long limit) {
            reentrantReadWriteLock.writeLock().lock();
            try {
                this.limit = limit;
            } catch (Exception e){
                this.limit = null;
            } finally {
                reentrantReadWriteLock.writeLock().unlock();
            }
        }
    
        public Long getLimit() {
            reentrantReadWriteLock.readLock().lock();
            Long limit;
            try {
                limit = this.limit;
            } catch (Exception e){
                limit = null;
            } finally {
                reentrantReadWriteLock.readLock().unlock();
            }
            return limit;
        }
    
    }
    
    
    package metodo_java8_moderno;
    
    import java.util.ArrayList;
    import java.util.List;
    
    public class MultiThreadClass {
    
        public SynchronizedObjects so;
    
        public List<Object> editAndAdd(List<List<Long>> matrix){
            this.so = new SynchronizedObjects(
                    matrix,
                    (long) matrix.get(0).size()*matrix.size()/2,
                    0,
                    new ArrayList<>()
            );
            int core = Runtime.getRuntime().availableProcessors();
            for(int i=0; i<matrix.get(0).size(); i++){
                so.setI(i);
                ForkJoinListMutator listMutator = ForkJoinListMutator.getDefault();
                listMutator.mutate(so.getMatrix(),Math.max(1,so.getMatrix().size()/(core*1+1)),(j) -> parallelFor(j));
            }
            List<Object> objectList = new ArrayList<>();
            objectList.add(so.getMatrix());
            objectList.add(so.getNumbers());
            return objectList;
        }
    
        public void parallelFor(int j){
            try{
                List<List<Long>> matrix = so.getMatrix();
                int i = so.getI();
                //List<Long> numbers = so.getNumbers();
                Long limit = so.getLimit();
                if(matrix.get(j).get(i).longValue() <= limit){
                    so.addNumbers(matrix.get(j).get(i));
                    //numbers.add();
                    matrix.get(j).set(i,null);
                }
                //so.setNumbers(numbers);
            }catch (Exception e){
                System.out.println("Errore!");
                System.out.println(e.getMessage());
                System.out.println(e.getCause());
            }
        }
    
    }
    
    In ogni caso il codice sarebbe troppo verboso e praticamente ingestibile.
  • Re: Creare un codice indipendente ed immutabile per far lavorare in multithreading un ciclo for parallelizzabile

    iBaffiPro ha scritto:


    perché l'obiettivo è tenere assolutamente questo pezzo invariato:
    
                    if(matrix.get(j).get(i).longValue() <= limit){
                        numbers.add(matrix.get(j).get(i));
                        matrix.get(j).set(i,null);
                    }
    
    E questo "assolutamente" in che senso? PERCHÉ??

    iBaffiPro ha scritto:


    Il problema è numbers, non matrix. matrix viene editata correttamente.
    Allora, ho guardato un attimo bene e .... direi che ho capito. Ma la questione è MOLTO più ampia e devo descrivertela meglio.

    Il concetto del Fork/Join può essere utilizzato per velocizzare svariati tipi di operazioni: ad esempio per ordinare una lista, per fare ricerche, per effettuare una "riduzione" (da N valori ad un solo valore, es. il concetto di sum(lista) ), ecc...

    Se prendiamo proprio il sum(), si ha una cosa tipo (esempio):
      6   8   4   10   6  16   9   2   12
      \       /   \        /   \        /
       \     /     \      /     \      /
       Thread1     Thread2      Thread3
      calcola 18  calcola 32   calcola 23
          \           /            |
           \         /             |
            \       /              |
           "join" 18+32            |
               =50                 /
                 \                /
                  \              /
                   \            /
                    "join" 50+23
                         =73
    Se ben ricordi, nel metodo compute() del task del Fork/Join pool si fa in generale una cosa di questo tipo:
    protected XYZ compute() {
        if (se_il_task_è_piccolo) {
            // computa sequenzialmente
            return // il risultato di questa computazione
        } else {
            // crea leftTask
            leftTask.fork();
            // crea rightTask
            XYZ risultatoRight = rightTask.compute();
            XYZ risultatoLeft = leftTask.join();
            return // qui COMBINA risultatoLeft e risultatoRight
        }
    Questo chiaramente se il F/J si usasse per applicare il concetto di "riduzione", che poi in pratica è quella fase finale del metodo in cui, come vedi, COMBINA il risultato dei due sotto task left e right.

    Nel ForkJoinListMutator che avevo scritto io e che ti avevo ampiamente mostrato, l'obiettivo NON era quello di fare una "riduzione", ma solo di spezzare una "grossa" lista in tanti pezzi e fare in modo che la MUTAZIONE degli elementi della lista fosse ben parallelizzabile, appunto con il F/J pool. Quindi lì nel mio codice NON c'è una fase finale in cui "combina" due risultati.

    Quindi, FINTANTO che la implementazione del ElementMutator<T> va a toccare SOLO l'elemento i-esimo della lista, questo non causa alcun problema ed è tutto corretto. Questo era (è) l'obiettivo del mio ForkJoinListMutator.

    Ora, il TUO caso. Tu con il "mio" ForkJoinListMutator non stai solo usando/modificando l'elemento i-esimo ma .... stai anche MUTANDO un oggetto "condiviso", quel SynchronizedObjects.

    Nel SynchronizedObjects hai messo sì la sincronizzazione, con il ReentrantReadWriteLock, quindi la classe di per sé è thread-safe, cioè i metodi sono "atomici" e causano mutua-esclusione tra thread. (non ho verificato tutto per bene ma a vista pare ok). Se un thread A chiama so.setNumbers(...) in quel frangente nessun altro thread può fare operazioni su quello stesso oggetto so. Quindi NON è questo il problema.

    Il problema è più fine: nel parallelFor fai un so.getNumbers() quasi all'inizio e poi un so.setNumbers(numbers) alla fine (li hai commentati ma volevi farli! ).
    Quale è il problema? Il problema è che parallelFor è usato da thread differenti. Un elemento i-esimo è trattato da un SOLO thread ma tu non sai COME saranno intercalate le operazioni di parallelFor tra i thread. Potrebbero fare:
    Thread1          Thread2
    so.getNumbers()
                     so.getNumbers()
           ..........
    so.setNumbers()
                     so.setNumbers()
    oppure
    Thread1          Thread2
    so.getNumbers()
                     so.getNumbers()
           ..........
                     so.setNumbers()
    so.setNumbers()
    oppure ecc.....

    Quindi che cosa setti??? NON LO SAI! Dipende dalle tempistiche più o meno (s)fortunate dei thread. E il problema è proprio quello: la mutazione di un oggetto condiviso che viene usato nel contesto di tutti i thread del F/J pool.

    E sai perché stai facendo tutti questi casini? Perché non hai le idee chiare sul multi-threading/concorrenza. E in generale non hai sufficienti basi su Java. Oltretutto stai facendo anche stupidaggini tipo:
        public void setNumbers(List<Long> numbers) {
            reentrantReadWriteLock.writeLock().lock();
            try {
                this.numbers = numbers;
            } catch (Exception e){
                this.numbers = null;
            } finally {
                reentrantReadWriteLock.writeLock().unlock();
            }
        }
    Ti rendi conto che un assegnamento come this.numbers = numbers; NON causa eccezioni quindi quel catch è totalmente inutile?

    Lo so, te lo sto già dicendo da tempo e mi spiace ripeterlo. Ma secondo me queste tue esercitazioni sono abbastanza, molto inutili, ti stanno portando via davvero un sacco di tempo .... tempo che invece potresti dedicare (meglio) a leggere un bel libro su Java ....
    Insomma ... stai proprio solo "giocherellando" con Java ....

    P.S. Guarda, io in queste settimane ho ripreso a studiare Kotlin (un altro linguaggio per la JVM), che purtroppo non lo uso per lavoro quindi ogni tanto devo sempre riprendere quasi dall'inizio. Sabato, l'altro ieri, mi sono messo a giocherellare un po' con http4k, un toolkit per fare webapp/web-service in Kotlin.
    Sì ma io ho 2 certificazioni su Java e 13+ anni di esperienza, quindi se permetti, credo che posso "giocherellare" con queste cose ....
  • Re: Creare un codice indipendente ed immutabile per far lavorare in multithreading un ciclo for parallelizzabile

    Il codice:
                    if(matrix.get(j).get(i).longValue() <= limit){
                        numbers.add(matrix.get(j).get(i));
                        matrix.get(j).set(i,null);
                    }
    non deve essere assolutamente modificato per diverse ragioni:
    1) Il codice non esiste, è una mia invenzione e per la verità neppure troppo intelligente perché impegna poco la CPU rendendo l’utilità del processo parallelo praticamente insensato;
    2) Il codice reale è diverso;
    3) Il metodo editAndAdd() è uno dei tanti che vorrei parallelizzare. Non posso correggere tutto il codice dentro SingleThreadClass.java. Il mio obiettivo è aggiungere codice a editAndAdd() trasformando il processo parallelo. Non posso riscrivere il codice per ogni metodo altrimenti dovrei ricominciare da capo l’esercitazione.
    Ho capito il discorso che hai fatto e conferma proprio il mio dubbio iniziale.
    A tuo avviso si risolve con synchronized()?
    synchronized() non saprei proprio come usaro con F/J, esiste come per i classici thread?
  • Re: Creare un codice indipendente ed immutabile per far lavorare in multithreading un ciclo for parallelizzabile

    iBaffiPro ha scritto:


    1) Il codice non esiste, è una mia invenzione e per la verità neppure troppo intelligente perché impegna poco la CPU rendendo l’utilità del processo parallelo praticamente insensato;
    2) Il codice reale è diverso;
    Ok, come avevo già detto, ovviamente io NON posso sapere quale sia il tuo codice reale .... se continui a postare codice che non è quello realistico ....
    E a questo punto non sarebbe nemmeno importante sapere cosa realmente sia ...

    iBaffiPro ha scritto:


    Il mio obiettivo è aggiungere codice a editAndAdd() trasformando il processo parallelo. Non posso riscrivere il codice per ogni metodo altrimenti dovrei ricominciare da capo l’esercitazione.
    Facciamo così: tu spieghi meglio quale è la questione, da cosa parti e a cosa vuoi arrivare. Ma SENZA postare codice.
    Hai una matrice, da quanto ho già visto, modellata come List<List<Long>> . Va bene. Io avevo suggerito di incapsularla in una classe ma NON è questa la questione importante (è più una questione di "design").

    Quindi: quante matrici così hai? Una sola? O molte? E stiamo parlando di quante righe/colonne? Sulle decine ... o migliaia?
    E in sostanza: che cosa vuoi estrarre/calcolare o modificare di tale/i matrici?

    E a quel punto ti dico cosa farei IO.

    iBaffiPro ha scritto:


    A tuo avviso si risolve con synchronized()?
    NO
  • Re: Creare un codice indipendente ed immutabile per far lavorare in multithreading un ciclo for parallelizzabile

    Ho una matrice 'matrix' modellata con List<List<Long>> (per la verità 2 ma ha poca importanza) che edito ed una lista di oggetti che produco che ho modellato per semplicità con List<Long> numbers. Il codice reale che ho è questo:
    
        public List<Object> metodoDaRendereParallelo(
                List<List<Long>> g, List<List<Long>> p, String a, String b, String c,
                Long d, Long e
        ){
            try {
    	    // a, b, c, d, e sono delle costanti
                // g e p sono le matrici che edito nel metodo
                // in metodoDaRendereParallelo() creo una nuova lista:
    	    List<Componente> componenti = new ArrayList<Componente>();
                // Componente è un normale oggetto che contiene stringhe e Long
        	    // 'componenti' viene riempita con valori calcolati a partire dalle matrici g e p
    	    // input + output vengono restituiti per essere elaborati da un metodo successivo
                // g ha un numero variabile di colonne (tra 4 e 30 circa) e molte righe (20000 circa)
                // p è simile a g
                // i tempi di elaborazione con 20000 righe sono di 5 ore circa ma è un tempo molto vario...
                List<Object> gpc = new ArrayList<Object>();
                gpc.add(g);
                gpc.add(p);
                gpc.add(componenti);
                return gpc;
            }catch (Exception e){
                return null;
            }
        }
    
    Il corpo del metodo non te lo posto perché è lungo e non lo voglio editare.
    A mio avviso ti conviene ragionare sullo script che ti ho postato. Se F/J funziona sul programmino che ho postato deve funzionare anche sul mio.
    Link per il download
    https://we.tl/t-wFvnFUJuh
    NOTA BENE: Questo pezzo è anche importante:
            for(int i=0; i<matrix.get(0).size(); i++){
                // ...
            }
    perché fa capire come il metodo che voglio parallelizzare non sia completamente parallelizzabile. I calcoli che eseguo devono avvenire in ordine, si parte dalla prima riga e poi alla seconda, ecc... non il viceversa.
  • Re: Creare un codice indipendente ed immutabile per far lavorare in multithreading un ciclo for parallelizzabile

    iBaffiPro ha scritto:


    Il codice reale che ho è questo:
    Che scritto così non mi dice assolutamente nulla ...

    iBaffiPro ha scritto:


    Link per il download
    https://we.tl/t-wFvnFUJuh
    E neanche questo è particolarmente utile ... (tra l'altro è sostanzialmente quello che hai già postato). Oltretutto ho guardato il ForkJoinListMutator ed ho notato che hai pure sbagliato a fare il compute() perché hai scritto:
                    MutateTask<T> leftTask = new MutateTask<>(list, start, start+length/2, seqThreshold, mutator);
                    leftTask.fork();
                    leftTask.join();
                    MutateTask<T> rightTask = new MutateTask<>(list, start+length/2, end, seqThreshold, mutator);
                    rightTask.compute();
    Questa non (ripeto NON) è la sequenza "ottimale" e se ci ragioni ci potresti anche arrivare. Non è nemmeno la copia di quello (giusto) scritto da me qui

    iBaffiPro ha scritto:


    NOTA BENE: Questo pezzo è anche importante:
            for(int i=0; i<matrix.get(0).size(); i++){
    perché fa capire come il metodo che voglio parallelizzare non sia completamente parallelizzabile. I calcoli che eseguo devono avvenire in ordine, si parte dalla prima riga e poi alla seconda, ecc... non il viceversa.
    Questa richiesta ha il suo senso solo se la necessità è di garantire quell'ordine ben preciso dei valori nel numbers.add(). Allora sì, ha assolutamente senso ed è ok.
    Ma in tal caso (e anche per quanto ho mostrato prima), il "mio" ForkJoinListMutator non è molto adatto in questo caso. Insomma l'avevo fatto per "altri" scopi.
    E comunque se hai matrici "piccole" es. 100x100 il multi-threading non serve a un piffero ...

    Ahhh, non l'avevo visto prima: hai pure usato CopyOnWriteArrayList che in quel contesto dei numbers è assolutamente sbagliato/inappropriato perché molto inefficiente. Sai quale è la peculiarità di CopyOnWriteArrayList e in QUALE scenario è molto utile???

    Mi spiace ma è parecchio difficile aiutarti viste le premesse e il tuo livello su Java.

    Lo indico ancora una volta: Il Nuovo Java - Claudio De Sio Cesari
  • Re: Creare un codice indipendente ed immutabile per far lavorare in multithreading un ciclo for parallelizzabile

    Forse il tuo codice non è adattabile a questo scenario però la dimensione della matrice è ininfluente. Il multi-threading si può applicare anche a matrici 10x10, su pochi dati possono essere fatti molti calcoli.
    Ho timore di non aver compreso bene il funzionamento di fork(), join() e compute(). Io ho capito questo:
    1) fork(), join() sulla parte sinistra che viene trasferita su un nuovo thread;
    2) compute() sulla parte destra della lista;
    leftTask.fork(): obbliga il sistema operativo ad eseguire leftTask su un nuovo thread (thread 2);
    leftTask.join(): riporta l'output di leftTask sul thread originario (thread 1);
    rightTask.compute(): obbliga rightTask a partire sul thread originario (thread 1) altrimenti resterebbe fermo.
    L'ordine perché è importante dato che parliamo di thread diversi?
Devi accedere o registrarti per scrivere nel forum
47 risposte