[Index Software] Coin des développeurs :]

Pour les gens qui ont simplement envie de discuter sans souhaiter faire passer d'information particulière.
Avatar du membre
Bubu
Intarissable
Messages : 7750
Enregistré le : dimanche 19 mai 2013 à 12:03
Localisation : En haut à gauche

Re: [Index] Coin des développeurs :]

Message par Bubu »

C'est fabuleux.
A quand une AI capable de compiler du code séquentiel, et de le transformer en code parallèle quand c'est possible ?
Il y a des ébauches, certes, mais c'est toujours très primitif. (Genre en utilisant les SIMDs ... il faudrait des AIs, capable de comprendre le code dans sa sémantique, et de le transformer en code parallèle.)
Mais on peut encore rêver.

Le code parallèle sur CPU (ou calcul distribué) est une vraie corvée, d'autant plus que maintenant c'est une obligation physique de paralléliser, de distribuer les calculs sur plusieurs cœurs (ou processeurs, ce qui revient au même).
Les deadlocks, venant d'une synchronisation des tâches male conçue, engendrent des freezes (blocage des processeurs à cause de deux tâches qui s'attendent mutuellement), des résultats faux, et le débogage est extrêmement difficile pour le calcul parallèle.
De plus, un usage mauvais des mécanismes de synchronisation peut engendrer tout l'inverse d'une optimisation multi threads. Mal employés, les threads peuvent passer plus de temps à s'attendre qu'à exécuter le code.
C'est une corvée à cause de l'obligation d'utiliser des mutexes, des sections critiques, des sémaphores, qui permettent de synchroniser les tâches entre elles. C'est très difficile de gérer ces mécanismes. Et sources de bugs ou de blocages, carrément.
La seule solution qu'on ait c'est d'être d'une rigueur stalinienne quand on les utilise. Il ne faut pas compter sur le débogage.
Et quand ça plante, c'est pas juste une image mal placée, c'est carrément le plantage fatal de l'application. Ou bien l'application ne plante pas, mais freeze (elle se gèle, se bloque), et dans ce cas on ne peut pas utiliser le débuggeur car l'appli ne plante pas à proprement parler, mais les OS détectent dans ce cas que l'application ne répond pas, et la seule chose à faire c'est de la shooter, de l'arrêter brutalement, ce qui n'est jamais bon pour la stabilité de l'OS.

(Après on peut quand même déboguer les deadlocks en suivant les piles d'appels (call stacks) de chaque thread pour voir où ils ont lieu, mais c'est franchement galère)

En Java, il y a trois possibilités pour synchroniser des tâches parallèles :

-La première, c'est le préfixe synchronized devant une méthode.
C'est comme si la méthode était encapsulée dans une section critique : seul un thread à la fois peut exécuter cette méthode.

-La deuxième, c'est le préfixe volatile devant la déclaration d'une variable (appelée aussi attribut ou propriété) de classe.
La variable ne peut être manipulée que par un thread à la fois. Cela reviendrait à utiliser une unique section critique sur les accesseurs de ladite variable. (une seule section critique pour les 2 accesseurs (getter et setter))
D'ailleurs dans ce cas il faudrait plutôt parler de mutex que de section critique, même si c'est la même chose finalement. Conceptuellement, une section critique ne s'applique qu'a une portion de code. En fait c'est plus puissant que ça, la section critique synchronise en utilisant son adresse, et non celle du code. Donc elle peut être utilisée pour des portions de code différentes en même temps, à la fois. On peut l'utiliser de manière unique pour plusieurs portions de codes. Le mutex n'est qu'une simple mise en forme objet d'une section critique.

-Enfin, tous les objets Java ont une méthode join(), qui permet d'attendre, et de savoir quand une tâche est terminée. Cette dernière solution n'est pas encore très claire pour moi, donc je ne peux pas
développer. Mais je vais me renseigner car j'en ai besoin actuellement. Tout ce que je sais, c'est que ça permet d'attendre que plusieurs tâches aient fini leur besogne avant de continuer le programme.
L'API C de Windows implémente les fonctions WaitForSingleObject() et WaitForMultipleObjets(), pour lesquelles il faut préciser le ou les thread(s) concerné(s). C'est équivalent : ça bloque le processus principal jusqu'à ce que tous les threads envoient un événement comme quoi ils ont fini.


Il y a aussi une solution alternative à volatile, c'est le préfixe atomic, quand on est sûr que la variable n'est accédée qu'en lecture. C'est beaucoup plus léger, et cela assure que la variable soit toujours lue en intégralité par chaque thread.
C'est comme si on mettait une section critique sur seulement l'accesseur getter.

Comment se passe la synchronisation de tâches ?
Quand un thread entre dans une section critique, il prévient l'OS que cette portion de code est "occupée" et si d'autres threads veulent l'exécuter, ils sont mis en pause. Et quand le thread sort de la section critique, l'OS réveille un nouveau thread, celui qui a le plus attendu en général, ou celui de priorité supérieure, ou les deux combinés (mais cela dépend de l'heuristique de l'OS), et qui était en attente.
C'est comme pour nous quand on va faire les courses en ce moment de confinement : un client à la fois dans le commerce.
Le client entre et fait sa popotte, tous les autres attendent. Dès qu'il sort du commerce, on laisse entrer un autre.

Juste pour préciser, c'est l'OS qui gère les sections critiques, mais c'est au développeur de les définir.
Le développeur n'a juste qu'à dire quand on entre et sort d'une section critique. Même bas-niveau, c'est l'OS qui gère tout en se débrouillant avec ça.
Sans rentrer dans les détails, on peut faire des objets appelés mutexes avec (je crois qu'en français on appelle ça des verrous), qui permettent de réguler l'accès aux différentes ressources partagées par plusieurs threads.

Le mot mutex est un mot valise pour "mutually exclusive" ( tasks), une tache exécute et rejette toute autre, qui est placée en attente. L'OS réveille l'une des tâches selon son heuristique quand la première tâche débloque le mutex.
C'est comme le portique dans le métro.

Un exemple d'implémentation C++ pour Windows :

Code : Tout sélectionner

#pragma once
#include <windows.h>

class Mutex
{
protected:
	CRITICAL_SECTION cs;
public:

	Mutex(void)
	{
		InitializeCriticalSection(&cs);
	}

	void lock()
	{
		EnterCriticalSection(&cs);
	}

	void unlock()
	{
		LeaveCriticalSection(&cs);
	}

	~Mutex(void)
	{
		DeleteCriticalSection(&cs);
	}
};
On voit bien que c'est juste une encapsulation sous forme d'objet qui se verrouille et se déverrouille, d'une section critique. Tout le reste est géré directement par l'OS.

Edit:
Il y a le langage OCAM, qui permet de coder en parallèle comme en série, sans s'embarrasser de ces mécanismes de synchronisation, mais il est trop bas niveau pour l'employer pour faire des jeux, ou des applications grand public...

Un exemple d'implémentation de boucle parallèle en C++ pour Windows :

Code : Tout sélectionner

#pragma once
#include <windows.h>
#include <vector>
using namespace std;

#include "criticalsection.h"

typedef void (*LOOP_FUNC)(int index,void * params);
typedef bool (*FIND_FUNC)(int index,void * params);

class MultiThreadedLoop
{
protected:
	bool exitRequested;
	bool indexFound;
	HANDLE doneEvent;
	int doneCount;
	int foundIndex;
	CriticalSection CScount;
	
	struct ThreadParams
	{
		int firstIndex;
		int firstOutIndex;
		HANDLE threadHandle;
		HANDLE startEvent;
		MultiThreadedLoop * pool;
	};

	static void find(ThreadParams * params)
	{
		FIND_FUNC f=params->pool->findFunction;
		void * p=params->pool->findFunctionParams;
		for (int i=params->firstIndex;i<params->firstOutIndex && !params->pool->indexFound;i++)
			if (f(i,p))
			{
				params->pool->foundIndex=i;
				params->pool->indexFound=true;
			}
	}
	
	static UINT threadFunction(ThreadParams * params)
	{
		while (true)
		{
			WaitForSingleObject(params->startEvent,INFINITE);
			if (params->pool->exitRequested) return 0;

			int i;

			switch(params->pool->mode)
			{
			case FIND:
				find(params);
				break;
			case LOOP:
				for (i=params->firstIndex;i<params->firstOutIndex;i++)
					params->pool->loopFunction(i,params->pool->loopFunctionParams);
				break;
			};

			params->pool->CScount.lock();
			params->pool->doneCount--;
			if (!params->pool->doneCount) SetEvent(params->pool->doneEvent);
			params->pool->CScount.unlock();
		}

		return 0;
	}

	vector<ThreadParams> threadParams;
	LOOP_FUNC loopFunction;
	FIND_FUNC findFunction;
	void * loopFunctionParams;
	void * findFunctionParams;
	enum MODE {LOOP,FIND} mode;
public:
	

	MultiThreadedLoop()
	{
	};

	~MultiThreadedLoop()
	{
		release();
	};

	bool init(int nbThreads=0)
	{
		if (!threadParams.empty()) release();

		SYSTEM_INFO systemInfo;
		GetSystemInfo(&systemInfo);

		if (nbThreads<1) nbThreads=systemInfo.dwNumberOfProcessors;

		doneEvent=CreateEvent(NULL,FALSE,FALSE,NULL);
		if (doneEvent==NULL) return false;

		exitRequested=false;

		threadParams.resize(nbThreads);

		for (int i=0;i<nbThreads;i++)
		{
			threadParams[i].startEvent=CreateEvent(NULL,FALSE,FALSE,NULL);
			threadParams[i].pool=this;

			threadParams[i].threadHandle=CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)threadFunction,&threadParams[i],0,NULL);

			if (threadParams[i].threadHandle==NULL || threadParams[i].startEvent==NULL)
			{
				exitRequested=true;

				for (int j=0;j<i;j++)
				{
					SetEvent(threadParams[j].startEvent);
					WaitForSingleObject(threadParams[j].threadHandle,INFINITE);
					CloseHandle(threadParams[j].threadHandle);
					CloseHandle(threadParams[j].startEvent);
				}

				if (threadParams[i].threadHandle!=NULL) CloseHandle(threadParams[i].threadHandle);
				if (threadParams[i].startEvent!=NULL) CloseHandle(threadParams[i].startEvent);
				
				threadParams.clear();

				exitRequested=false;
				CloseHandle(doneEvent);

				return false;
			}

			
			SetThreadPriority(threadParams[i].threadHandle,THREAD_PRIORITY_HIGHEST);
			SetThreadIdealProcessor(threadParams[i].threadHandle,i%systemInfo.dwNumberOfProcessors);
		}

		return true;
	}

	void release()
	{
		if (threadParams.empty()) return;

		exitRequested=true;
		for (int i=0;i<(int)threadParams.size();i++)
		{
			SetEvent(threadParams[i].startEvent);
			WaitForSingleObject(threadParams[i].threadHandle,INFINITE);
			CloseHandle(threadParams[i].threadHandle);
			CloseHandle(threadParams[i].startEvent);
		}
		threadParams.clear();
		exitRequested=false;
		CloseHandle(doneEvent);
	}

	void loop(LOOP_FUNC function,void * functionParams,int firstIndex,int firstOutIndex)
	{
		
		if (firstIndex>=firstOutIndex) return;

		mode=LOOP;

		int nbElements=firstOutIndex-firstIndex;

		int nbTasks=min((int)threadParams.size(),nbElements);

		int chunkSize=nbElements/nbTasks;

		int index=firstIndex;

		for (int i=0;i<nbTasks;i++)
		{
			threadParams[i].firstIndex=index;
			index+=chunkSize;
			threadParams[i].firstOutIndex=index;
		}

		threadParams.back().firstOutIndex=firstOutIndex;

		loopFunction=function;
		loopFunctionParams=functionParams;

		doneCount=nbTasks;
		ResetEvent(doneEvent);
		
		for (int i=0;i<nbTasks;i++)
			SetEvent(threadParams[i].startEvent);

		WaitForSingleObject(doneEvent,INFINITE);
	}

	bool find(FIND_FUNC function,void * functionParams,int firstIndex,int firstOutIndex,int * foundIndex=NULL)
	{
		if (firstIndex>=firstOutIndex) return false;

		mode=FIND;
		indexFound=false;

		int nbElements=firstOutIndex-firstIndex;

		int nbTasks=min((int)threadParams.size(),nbElements);

		int chunkSize=nbElements/nbTasks;

		int index=firstIndex;

		for (int i=0;i<nbTasks;i++)
		{
			threadParams[i].firstIndex=index;
			index+=chunkSize;
			threadParams[i].firstOutIndex=index;
		}

		threadParams.back().firstOutIndex=firstOutIndex;

		findFunction=function;
		findFunctionParams=functionParams;

		doneCount=nbTasks;
		ResetEvent(doneEvent);
		
		for (int i=0;i<nbTasks;i++)
			SetEvent(threadParams[i].startEvent);

		WaitForSingleObject(doneEvent,INFINITE);

		if (foundIndex!=NULL) *foundIndex=this->foundIndex;

		return indexFound;
	}
};
La boucle est dispatchée sur les différents coeurs du CPU. (C++, Windows)

Cet objet permet d'exécuter une boucle sur des calculs indépendants, sur plusieurs threads. On a une boucle de 12 itérations, et bien on la dispatche sur 4 threads par exemple, chacun exécutant seulement 3 itérations. Donc un gain de 400% en performance. Bien sur, c'est seulement rentable pour des grosses (longues) boucles de calcul indépendant.
Je l'avais créé pour trier les polygones transparents en 3D.
Modifié en dernier par Bubu le vendredi 1 mai 2020 à 13:39, modifié 1 fois.
TSA, diagnostic établi à mes 33 ans par le CRA de ma région.
"Ce syndrome est caractérisé chez ce patient par l’absence de détérioration intellectuelle, un syndrome dysexécutif, un déficit d'attention"
Avatar du membre
Tugdual
Modérateur
Messages : 41279
Enregistré le : jeudi 15 novembre 2012 à 0:13
Localisation : Nord-44

Re: [Index] Coin des développeurs :]

Message par Tugdual »

Microsoft annonce Rust/WinRT :
Il va falloir que je me décide à regarder RUST de plus près...
TCS = trouble de la communication sociale (24/09/2014).
Avatar du membre
Tugdual
Modérateur
Messages : 41279
Enregistré le : jeudi 15 novembre 2012 à 0:13
Localisation : Nord-44

Re: [Index] Coin des développeurs :]

Message par Tugdual »

Ces bouts de codes open source obsolètes sont utilisés un peu partout :
TCS = trouble de la communication sociale (24/09/2014).
Avatar du membre
Bubu
Intarissable
Messages : 7750
Enregistré le : dimanche 19 mai 2013 à 12:03
Localisation : En haut à gauche

Re: [Index] Coin des développeurs :]

Message par Bubu »

Bubu a écrit : mercredi 22 avril 2020 à 22:54 C'est fabuleux.
A quand une AI capable de compiler du code séquentiel, et de le transformer en code parallèle quand c'est possible ?
Il y a des ébauches, certes, mais c'est toujours très primitif. (Genre en utilisant les SIMDs ... il faudrait des AIs, capable de comprendre le code dans sa sémantique, et de le transformer en code parallèle.)
Mais on peut encore rêver.

Le code parallèle sur CPU (ou calcul distribué) est une vraie corvée, d'autant plus que maintenant c'est une obligation physique de paralléliser, de distribuer les calculs sur plusieurs cœurs (ou processeurs, ce qui revient au même).
Les deadlocks, venant d'une synchronisation des tâches male conçue, engendrent des freezes (blocage des processeurs à cause de deux tâches qui s'attendent mutuellement), des résultats faux, et le débogage est extrêmement difficile pour le calcul parallèle.
De plus, un usage mauvais des mécanismes de synchronisation peut engendrer tout l'inverse d'une optimisation multi threads. Mal employés, les threads peuvent passer plus de temps à s'attendre qu'à exécuter le code.
C'est une corvée à cause de l'obligation d'utiliser des mutexes, des sections critiques, des sémaphores, qui permettent de synchroniser les tâches entre elles. C'est très difficile de gérer ces mécanismes. Et sources de bugs ou de blocages, carrément.
La seule solution qu'on ait c'est d'être d'une rigueur stalinienne quand on les utilise. Il ne faut pas compter sur le débogage.
Et quand ça plante, c'est pas juste une image mal placée, c'est carrément le plantage fatal de l'application. Ou bien l'application ne plante pas, mais freeze (elle se gèle, se bloque), et dans ce cas on ne peut pas utiliser le débuggeur car l'appli ne plante pas à proprement parler, mais les OS détectent dans ce cas que l'application ne répond pas, et la seule chose à faire c'est de la shooter, de l'arrêter brutalement, ce qui n'est jamais bon pour la stabilité de l'OS.

(Après on peut quand même déboguer les deadlocks en suivant les piles d'appels (call stacks) de chaque thread pour voir où ils ont lieu, mais c'est franchement galère)

En Java, il y a trois possibilités pour synchroniser des tâches parallèles :

-La première, c'est le préfixe synchronized devant une méthode.
C'est comme si la méthode était encapsulée dans une section critique : seul un thread à la fois peut exécuter cette méthode.

-La deuxième, c'est le préfixe volatile devant la déclaration d'une variable (appelée aussi attribut ou propriété) de classe.
La variable ne peut être manipulée que par un thread à la fois. Cela reviendrait à utiliser une unique section critique sur les accesseurs de ladite variable. (une seule section critique pour les 2 accesseurs (getter et setter))
D'ailleurs dans ce cas il faudrait plutôt parler de mutex que de section critique, même si c'est la même chose finalement. Conceptuellement, une section critique ne s'applique qu'a une portion de code. En fait c'est plus puissant que ça, la section critique synchronise en utilisant son adresse, et non celle du code. Donc elle peut être utilisée pour des portions de code différentes en même temps, à la fois. On peut l'utiliser de manière unique pour plusieurs portions de codes. Le mutex n'est qu'une simple mise en forme objet d'une section critique.

-Enfin, tous les objets Java ont une méthode join(), qui permet d'attendre, et de savoir quand une tâche est terminée. Cette dernière solution n'est pas encore très claire pour moi, donc je ne peux pas
développer. Mais je vais me renseigner car j'en ai besoin actuellement. Tout ce que je sais, c'est que ça permet d'attendre que plusieurs tâches aient fini leur besogne avant de continuer le programme.
L'API C de Windows implémente les fonctions WaitForSingleObject() et WaitForMultipleObjets(), pour lesquelles il faut préciser le ou les thread(s) concerné(s). C'est équivalent : ça bloque le processus principal jusqu'à ce que tous les threads envoient un événement comme quoi ils ont fini.


Il y a aussi une solution alternative à volatile, c'est le préfixe atomic, quand on est sûr que la variable n'est accédée qu'en lecture. C'est beaucoup plus léger, et cela assure que la variable soit toujours lue en intégralité par chaque thread.
C'est comme si on mettait une section critique sur seulement l'accesseur getter.

Comment se passe la synchronisation de tâches ?
Quand un thread entre dans une section critique, il prévient l'OS que cette portion de code est "occupée" et si d'autres threads veulent l'exécuter, ils sont mis en pause. Et quand le thread sort de la section critique, l'OS réveille un nouveau thread, celui qui a le plus attendu en général, ou celui de priorité supérieure, ou les deux combinés (mais cela dépend de l'heuristique de l'OS), et qui était en attente.
C'est comme pour nous quand on va faire les courses en ce moment de confinement : un client à la fois dans le commerce.
Le client entre et fait sa popotte, tous les autres attendent. Dès qu'il sort du commerce, on laisse entrer un autre.

Juste pour préciser, c'est l'OS qui gère les sections critiques, mais c'est au développeur de les définir.
Le développeur n'a juste qu'à dire quand on entre et sort d'une section critique. Même bas-niveau, c'est l'OS qui gère tout en se débrouillant avec ça.
Sans rentrer dans les détails, on peut faire des objets appelés mutexes avec (je crois qu'en français on appelle ça des verrous), qui permettent de réguler l'accès aux différentes ressources partagées par plusieurs threads.

Le mot mutex est un mot valise pour "mutually exclusive" ( tasks), une tache exécute et rejette toute autre, qui est placée en attente. L'OS réveille l'une des tâches selon son heuristique quand la première tâche débloque le mutex.
C'est comme le portique dans le métro.

Un exemple d'implémentation C++ pour Windows :

Code : Tout sélectionner

#pragma once
#include <windows.h>

class Mutex
{
protected:
	CRITICAL_SECTION cs;
public:

	Mutex(void)
	{
		InitializeCriticalSection(&cs);
	}

	void lock()
	{
		EnterCriticalSection(&cs);
	}

	void unlock()
	{
		LeaveCriticalSection(&cs);
	}

	~Mutex(void)
	{
		DeleteCriticalSection(&cs);
	}
};
On voit bien que c'est juste une encapsulation sous forme d'objet qui se verrouille et se déverrouille, d'une section critique. Tout le reste est géré directement par l'OS.

Edit:
Il y a le langage OCAM, qui permet de coder en parallèle comme en série, sans s'embarrasser de ces mécanismes de synchronisation, mais il est trop bas niveau pour l'employer pour faire des jeux, ou des applications grand public...

Un exemple d'implémentation de boucle parallèle en C++ pour Windows :

Code : Tout sélectionner

#pragma once
#include <windows.h>
#include <vector>
using namespace std;

#include "criticalsection.h"

typedef void (*LOOP_FUNC)(int index,void * params);
typedef bool (*FIND_FUNC)(int index,void * params);

class MultiThreadedLoop
{
protected:
	bool exitRequested;
	bool indexFound;
	HANDLE doneEvent;
	int doneCount;
	int foundIndex;
	CriticalSection CScount;
	
	struct ThreadParams
	{
		int firstIndex;
		int firstOutIndex;
		HANDLE threadHandle;
		HANDLE startEvent;
		MultiThreadedLoop * pool;
	};

	static void find(ThreadParams * params)
	{
		FIND_FUNC f=params->pool->findFunction;
		void * p=params->pool->findFunctionParams;
		for (int i=params->firstIndex;i<params->firstOutIndex && !params->pool->indexFound;i++)
			if (f(i,p))
			{
				params->pool->foundIndex=i;
				params->pool->indexFound=true;
			}
	}
	
	static UINT threadFunction(ThreadParams * params)
	{
		while (true)
		{
			WaitForSingleObject(params->startEvent,INFINITE);
			if (params->pool->exitRequested) return 0;

			int i;

			switch(params->pool->mode)
			{
			case FIND:
				find(params);
				break;
			case LOOP:
				for (i=params->firstIndex;i<params->firstOutIndex;i++)
					params->pool->loopFunction(i,params->pool->loopFunctionParams);
				break;
			};

			params->pool->CScount.lock();
			params->pool->doneCount--;
			if (!params->pool->doneCount) SetEvent(params->pool->doneEvent);
			params->pool->CScount.unlock();
		}

		return 0;
	}

	vector<ThreadParams> threadParams;
	LOOP_FUNC loopFunction;
	FIND_FUNC findFunction;
	void * loopFunctionParams;
	void * findFunctionParams;
	enum MODE {LOOP,FIND} mode;
public:
	

	MultiThreadedLoop()
	{
	};

	~MultiThreadedLoop()
	{
		release();
	};

	bool init(int nbThreads=0)
	{
		if (!threadParams.empty()) release();

		SYSTEM_INFO systemInfo;
		GetSystemInfo(&systemInfo);

		if (nbThreads<1) nbThreads=systemInfo.dwNumberOfProcessors;

		doneEvent=CreateEvent(NULL,FALSE,FALSE,NULL);
		if (doneEvent==NULL) return false;

		exitRequested=false;

		threadParams.resize(nbThreads);

		for (int i=0;i<nbThreads;i++)
		{
			threadParams[i].startEvent=CreateEvent(NULL,FALSE,FALSE,NULL);
			threadParams[i].pool=this;

			threadParams[i].threadHandle=CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)threadFunction,&threadParams[i],0,NULL);

			if (threadParams[i].threadHandle==NULL || threadParams[i].startEvent==NULL)
			{
				exitRequested=true;

				for (int j=0;j<i;j++)
				{
					SetEvent(threadParams[j].startEvent);
					WaitForSingleObject(threadParams[j].threadHandle,INFINITE);
					CloseHandle(threadParams[j].threadHandle);
					CloseHandle(threadParams[j].startEvent);
				}

				if (threadParams[i].threadHandle!=NULL) CloseHandle(threadParams[i].threadHandle);
				if (threadParams[i].startEvent!=NULL) CloseHandle(threadParams[i].startEvent);
				
				threadParams.clear();

				exitRequested=false;
				CloseHandle(doneEvent);

				return false;
			}

			
			SetThreadPriority(threadParams[i].threadHandle,THREAD_PRIORITY_HIGHEST);
			SetThreadIdealProcessor(threadParams[i].threadHandle,i%systemInfo.dwNumberOfProcessors);
		}

		return true;
	}

	void release()
	{
		if (threadParams.empty()) return;

		exitRequested=true;
		for (int i=0;i<(int)threadParams.size();i++)
		{
			SetEvent(threadParams[i].startEvent);
			WaitForSingleObject(threadParams[i].threadHandle,INFINITE);
			CloseHandle(threadParams[i].threadHandle);
			CloseHandle(threadParams[i].startEvent);
		}
		threadParams.clear();
		exitRequested=false;
		CloseHandle(doneEvent);
	}

	void loop(LOOP_FUNC function,void * functionParams,int firstIndex,int firstOutIndex)
	{
		
		if (firstIndex>=firstOutIndex) return;

		mode=LOOP;

		int nbElements=firstOutIndex-firstIndex;

		int nbTasks=min((int)threadParams.size(),nbElements);

		int chunkSize=nbElements/nbTasks;

		int index=firstIndex;

		for (int i=0;i<nbTasks;i++)
		{
			threadParams[i].firstIndex=index;
			index+=chunkSize;
			threadParams[i].firstOutIndex=index;
		}

		threadParams.back().firstOutIndex=firstOutIndex;

		loopFunction=function;
		loopFunctionParams=functionParams;

		doneCount=nbTasks;
		ResetEvent(doneEvent);
		
		for (int i=0;i<nbTasks;i++)
			SetEvent(threadParams[i].startEvent);

		WaitForSingleObject(doneEvent,INFINITE);
	}

	bool find(FIND_FUNC function,void * functionParams,int firstIndex,int firstOutIndex,int * foundIndex=NULL)
	{
		if (firstIndex>=firstOutIndex) return false;

		mode=FIND;
		indexFound=false;

		int nbElements=firstOutIndex-firstIndex;

		int nbTasks=min((int)threadParams.size(),nbElements);

		int chunkSize=nbElements/nbTasks;

		int index=firstIndex;

		for (int i=0;i<nbTasks;i++)
		{
			threadParams[i].firstIndex=index;
			index+=chunkSize;
			threadParams[i].firstOutIndex=index;
		}

		threadParams.back().firstOutIndex=firstOutIndex;

		findFunction=function;
		findFunctionParams=functionParams;

		doneCount=nbTasks;
		ResetEvent(doneEvent);
		
		for (int i=0;i<nbTasks;i++)
			SetEvent(threadParams[i].startEvent);

		WaitForSingleObject(doneEvent,INFINITE);

		if (foundIndex!=NULL) *foundIndex=this->foundIndex;

		return indexFound;
	}
};
La boucle est dispatchée sur les différents coeurs du CPU. (C++, Windows)

Cet objet permet d'exécuter une boucle sur des calculs indépendants, sur plusieurs threads. On a une boucle de 12 itérations, et bien on la dispatche sur 4 threads par exemple, chacun exécutant seulement 3 itérations. Donc un gain de 400% en performance. Bien sur, c'est seulement rentable pour des grosses (longues) boucles de calcul indépendant.
Je l'avais créé pour trier les polygones transparents en 3D.
Par sur que cela soit intéressant, ou utile, notamment parce qu'il y a des algorithmes 3D qui permettent d'afficher correctement les polygones transparents sans les trier maintenant. (OIT = order independant transparency)

Je transformais sur le cpu les triangles pour les placer selon la caméra, je faisais la moyenne des profondeurs. Ensuite, un peu à l'image des tables de hash, je les mettais dans une table selon la catégorie de profondeur. Par exemple case 1 : de 0 à 100 mètres, case 2 : de 100 à 500 mètres, ainsi de suite. Ensuite en parallèle, grâce à cet objet (MultiThreadedLoop), je triais les différentes listes en même temps. Je remettais cela ensemble sous la forme d'un unique mesh (objet 3D), et je l'affichais. Car les GPUs préservent malgré leur parallélisme, la liste ordonnée de triangles à afficher. Leur ordre est gardé tel qu'il est donné, car le parallélisme se fait aux niveaux supérieurs.
Pour faire simple, si on donne à un GPU un triangle à tracer, puis un autre, on a la certitude absolue que le second polygone sera tracé après le premier.
Et c'est vrai depuis l'avènement des premières cartes graphiques, qu'on appelait cartes 3D... C'est une norme imposée par la R&D depuis le début, bien avant les shaders programmables.
TSA, diagnostic établi à mes 33 ans par le CRA de ma région.
"Ce syndrome est caractérisé chez ce patient par l’absence de détérioration intellectuelle, un syndrome dysexécutif, un déficit d'attention"
Avatar du membre
Manichéenne
Modératrice
Messages : 5040
Enregistré le : jeudi 24 octobre 2013 à 9:54

Re: [Index] Coin des développeurs :]

Message par Manichéenne »

J'ai passé des heures aujourd'hui à chercher pourquoi après une mise à jour mineure mes données n'étaient plus insérées dans une table. Pas d'erreur remontée, les traces n'indiquaient rien...
En fait je regardais dans la mauvaise base de données, qui contenait une table avec le même nom et la même structure. :oops:
Règle importante du développement : tout vérifier attentivement et ne pas faire trop vite. :roll:
Diagnostiquée Autiste Asperger et TDA.
Mère de 3 enfants : fils Aîné TDAH et TSA atypique, cadet TSA de type Asperger, benjamin en cours d'évaluation neuropsy.
Avatar du membre
Bubu
Intarissable
Messages : 7750
Enregistré le : dimanche 19 mai 2013 à 12:03
Localisation : En haut à gauche

Re: [Index] Coin des développeurs :]

Message par Bubu »

Manichéenne a écrit : lundi 18 mai 2020 à 21:41 J'ai passé des heures aujourd'hui à chercher pourquoi après une mise à jour mineure mes données n'étaient plus insérées dans une table. Pas d'erreur remontée, les traces n'indiquaient rien...
En fait je regardais dans la mauvaise base de données, qui contenait une table avec le même nom et la même structure. :oops:
Règle importante du développement : tout vérifier attentivement et ne pas faire trop vite. :roll:
Ah la joie du débogage, quant elle nous tient !
Bonjour au passage, ça faisait un bail ! :kiss:
C'est vrai que si on veut éviter au maximum le fléau du débogage, il faut être d'une rigueur drastique. Mais le débogage est inévitable.
Content que tu ais résolu le problème.
TSA, diagnostic établi à mes 33 ans par le CRA de ma région.
"Ce syndrome est caractérisé chez ce patient par l’absence de détérioration intellectuelle, un syndrome dysexécutif, un déficit d'attention"
Avatar du membre
Tugdual
Modérateur
Messages : 41279
Enregistré le : jeudi 15 novembre 2012 à 0:13
Localisation : Nord-44

Re: [Index] Coin des développeurs :]

Message par Tugdual »

TCS = trouble de la communication sociale (24/09/2014).
Avatar du membre
Tugdual
Modérateur
Messages : 41279
Enregistré le : jeudi 15 novembre 2012 à 0:13
Localisation : Nord-44

Re: [Index] Coin des développeurs :]

Message par Tugdual »

Le métier de développeur :
TCS = trouble de la communication sociale (24/09/2014).
Avatar du membre
Tugdual
Modérateur
Messages : 41279
Enregistré le : jeudi 15 novembre 2012 à 0:13
Localisation : Nord-44

Re: [Index] Coin des développeurs :]

Message par Tugdual »

Pour les geeks, justement :
TCS = trouble de la communication sociale (24/09/2014).
Avatar du membre
Tugdual
Modérateur
Messages : 41279
Enregistré le : jeudi 15 novembre 2012 à 0:13
Localisation : Nord-44

Re: [Index] Coin des développeurs :]

Message par Tugdual »

Et pour rester sur le thème historique :
TCS = trouble de la communication sociale (24/09/2014).
Avatar du membre
Tugdual
Modérateur
Messages : 41279
Enregistré le : jeudi 15 novembre 2012 à 0:13
Localisation : Nord-44

Re: [Index] Coin des développeurs :]

Message par Tugdual »

Une histoire de cordonniers pas très bien chaussés :
TCS = trouble de la communication sociale (24/09/2014).
Avatar du membre
Tugdual
Modérateur
Messages : 41279
Enregistré le : jeudi 15 novembre 2012 à 0:13
Localisation : Nord-44

Re: [Index] Coin des développeurs :]

Message par Tugdual »

TCS = trouble de la communication sociale (24/09/2014).
Avatar du membre
Tugdual
Modérateur
Messages : 41279
Enregistré le : jeudi 15 novembre 2012 à 0:13
Localisation : Nord-44

Re: [Index] Coin des développeurs :]

Message par Tugdual »

TCS = trouble de la communication sociale (24/09/2014).
Avatar du membre
Tugdual
Modérateur
Messages : 41279
Enregistré le : jeudi 15 novembre 2012 à 0:13
Localisation : Nord-44

Re: [Index] Software : Coin des développeurs :]

Message par Tugdual »

Les développeurs semblent d'accord à l'unanimité :
TCS = trouble de la communication sociale (24/09/2014).
Avatar du membre
Tugdual
Modérateur
Messages : 41279
Enregistré le : jeudi 15 novembre 2012 à 0:13
Localisation : Nord-44

Re: [Index] Software : Coin des développeurs :]

Message par Tugdual »

Pourquoi les dirigeants ont commencé :
TCS = trouble de la communication sociale (24/09/2014).