TP-systemes-concurrents/TP1/IncrMes.java

189 lines
7.5 KiB
Java
Raw Normal View History

2023-06-21 18:19:26 +00:00
import java.util.concurrent.atomic.AtomicLong;
// V0.3 - PM 17/09/21
interface Incrémenteur extends Runnable{
void incr();
/* Boucle d'incrémentation effectuée par un processus (thread).
* Le principe/objectif est de réaliser cette boucle, comportant un même nombre
* d'incrémentations en suivant différents schémas pour maintenir la cohérence
* (ou pas) et de comparer ces schémas en termes de correction et d'efficacité
*/
}
public class IncrMes {
// static long cpt = 0L;
static volatile long cpt = 0L;
// static final long NB_IT = 1000L; //Nb d'itérations de la boucle externe
// static final long NB_IT_INTERNES = 1000000L; //Nb d'itérations de la boucle interne
static final long NB_IT = 10L; //Nb d'itérations de la boucle externe
static final long NB_IT_INTERNES = 100L; //Nb d'itérations de la boucle interne
static long Attente_ms = 2L;
static final int Attente_nano = 0;
static Thread[] activités; // Tableau des processus
static Object mutex = new Object(); // pour les blocs synchronized
static void lancer(int nbA, Incrémenteur r) {
// Initialisation du/des compteur(s) incrémentés
cpt = 0L;
/* Création et lancement des threads.
* Chaque thread exécute le même code, qui est la méthode run() d'une classe
* implantant un Incrémenteur (autrement dit un schéma de réalisation particulier
* de la boucle dincrémentation)
*/
for (int i = 0; i < nbA ; i++) {
try {
IncrMes.activités[i] = new Thread(r,"t"+i);
}
catch (Exception e)
{
System.out.println(e);
}
IncrMes.activités[i].start();
}
}
static void finir() {
// Attente de la terminaison des différents threads lancés
for (int i = 0; i < IncrMes.activités.length ; i++) {
try {
IncrMes.activités[i].join();
}
catch (InterruptedException e)
{
System.out.println(e);
}
}
}
public static void main(String[] args) {
int nbActivités = 3;
int nbMesures = 6;
int i = 0;
long départ, fin;
boolean mutex = true;
// Chargement des paramètres saisis en ligne de commande
if (args.length == 3) {
try {
nbActivités = Integer.parseInt (args[0]);
Attente_ms = Integer.parseInt (args[1]);
nbMesures = Integer.parseInt (args[2]);
}
catch (NumberFormatException nfx) {
Attente_ms = 0;
}
if ((nbActivités < 1) || (Attente_ms < 1) || (nbMesures < 1)) {
System.out.println("Usage : IncrMes <Nb activités> <durée pause (ms)> <nbMesures>,"
+"avec Nb activités, durée pause, nbMesures >= 1");
System.exit (1);
}
} else {
System.out.println("Nb d'arguments ≠ 3. Exécution avec 6 mesures, 3 activités et 2 ms de pause");
}
activités = new Thread[nbActivités];
for (int it=1; it<=nbMesures; it++) { //
// Mesure itérée, pour éviter des écarts trop importants sur les premières mesures
// (du fait de possibles optimisations : processeur, cache, compilateur).
// Idée = retenir une moyenne des dernières mesures.
// Boucle séquentielle : NB_IT x NB_IT_INTERNES x nbActivités itérations
cpt=0L;
départ = System.nanoTime();
for (long li = 0; li < nbActivités*IncrMes.NB_IT; li++) {
for (long j = 1; j<=IncrMes.NB_IT_INTERNES; j++) {
IncrMes.cpt=IncrMes.cpt+j/j;
// j/j pour tenter d'éviter les optimisations du compilateur
}
try {
Thread.sleep(IncrMes.Attente_ms, IncrMes.Attente_nano);
// sleep pour modéliser un temps de calcul sans conflit d'accès et...
// pour tenter d'éviter les optimisations du cache
}
catch (InterruptedException ie)
{
System.out.println("InterruptedException : " + ie);
}
}
fin = System.nanoTime();
//System.out.print((fin-départ)/1000000L + ", ");
System.out.println("Durée exécution mono : " + (fin-départ)/1000000L);
System.out.println();
// Exemple de mesure avec IncrémenteurNonSync
départ = System.nanoTime();
// Lancement de nbActivités processus effectuant
// chacun NB_IT x NB_IT_INTERNES itérations
lancer(nbActivités, new IncrémenteurNonSync());
finir();
fin = System.nanoTime();
//System.out.print((fin-départ)/1000000L + ", ");
System.out.println("Durée exécution non synchronisée (ms): " + (fin-départ)/1000000L);
System.out.println();
// Compléter ici, sur le modèle précédent (lignes 112-120, (ou éditer la ligne 116)
//en définissant et mesurant différents Incrémenteur
// de manière analogue à ce qui est fait pour IncrémenteurNonSync
}
}
}
class IncrémenteurNonSync implements Incrémenteur {
// class IncrémenteurNonSync extends AtomicLong implements Incrémenteur {
/* Exemple d'incrémenteur :
-la méthode incr effectue les incrémentations sans gérer les conflis d'accès au compteur
- la méthode run() appelle incr()
*/
public synchronized void incr_sync(long j){
IncrMes.cpt=IncrMes.cpt+j/j;
}
public synchronized void interne(long i) {
for (long j = 1; j<=IncrMes.NB_IT_INTERNES; j++) {
// incrémentation élémentaire
IncrMes.cpt=IncrMes.cpt+j/j;
}
}
public void incr() {
// boucle imbriquée pour permettre (éventuellement) de tester différents
// grains de synchronisation
for (long i = 0L; i < IncrMes.NB_IT; i++) {
// boucle interne
for (long j = 1; j<=IncrMes.NB_IT_INTERNES; j++) {
// incrémentation élémentaire
incr_sync(j);
// IncrMes.cpt=IncrMes.cpt+j/j;
}
// interne(i);
try {
Thread.sleep(IncrMes.Attente_ms, IncrMes.Attente_nano);
}
// Attente depour éviter l'utilisation du cache et modéliser
// une partie du calcul sans conflit.
// Vous pouvez (éventuellement) commenter l'attente
// pour voir l'effet (intéressant) du mécanisme de cache,
// ou encore modifier la valeur de l'attente pour voir
// l'effet du grain de l'exclusion mutuelle sur les temps
// d'exécution
catch (InterruptedException ie)
{
System.out.println("InterruptedException : " + ie);
}
}
}
public void run() {
// afficher éventuellement la valeur du compteur avant/après
// pour vérifier la cohérence des incrémentations
System.out.println("Thread " + Thread.currentThread().getName() + " part de : " + IncrMes.cpt);
this.incr();
System.out.println("Thread " + Thread.currentThread().getName() + " finit à : " + IncrMes.cpt);
}
}