Supposons que nous ayons des fonctions telles que:parallélisation des processeurs d'emploi heterogeous
R1* process_input1(I1*);
R2* process_input2(I1*);
R1* process_input3(I2*);
//... etc
Ces fonctions sont des opérations à forte intensité de cpu qui prennent une quantité variable de temps. Cependant, ceux-ci peuvent tous fonctionner indépendamment les uns des autres et donc de bons candidats pour fonctionner en parallèle. Ils sont responsables de l'allocation de mémoire pour les types R (esult).
Nous avons aussi d'autres fonctions telles que:
void process_result1(R1*);
void process_result2(R1*);
void process_result3(R2*);
//... etc
Ces consomment le R (esult) s et sont responsables de désaffecter la mémoire qu'ils occupent.
La boucle principale est écrit comme suit:
void event_loop(Queue& some_queue)
{
while (job = some_queue.get_front())
{
switch(job.getCmdCode())
{
case CMD1:
R1* pResult = process_input1(job.getI1());
process_result1(pResult);
break;
case CMD2:
R2* pResult = process_input2(job.getI1());
process_result3(pResult);
break;
case CMD3:
R1* pResult = process_input3(job.getI2());
process_result2(pResult);
break;
//... etc
}
}
}
Comme vous pouvez le voir, les méthodes process_input
sont sérialisés. L'objectif est de paralléliser les méthodes process_input
pour améliorer le débit, tout en maintenant l'ordre dans lequel les méthodes process_result
sont appelées.
Nous avons donc concevoir une classe avec une interface comme ceci:
class ParallelSequencer
{
public:
ParallelSequencer(size_t nThreads);
template <typename I, typename R>
enqueue(I* input, R* (*process_input)(I*), void (*process_result)(R*));
};
La boucle principale devient:
void event_loop(Queue& some_queue)
{
ParallelSequencer sequencer(NUM_SEQUENCER_THREADS);
while (job = some_queue.get_front())
{
switch(job.getCmdCode())
{
case CMD1:
sequencer.enqueue(job.getI1(), process_input1, process_result1);
break;
case CMD2:
sequencer.enqueue(job.getI1(), process_input2, process_result3);
break;
case CMD3:
sequencer.enqueue(job.getI2(), process_input3, process_result2);
break;
//... etc
}
}
}
Pour mettre en œuvre, nous aurions besoin de faire la queue deux types de tuples: Mise en file d'attente lorsque enqueue() est appelée et supprimée en tant que thread récupère le travail.
template <typename R>
struct Output
{
R* data;
void (*process)(R*);
}
quand un file d'attente fil se fait appeler process_input()
et enlevé avant/après avoir appelé process_result()
. Comment déclarer, de manière sécurisée, une file d'attente contenant une séquence de ces deux structures de données?
Est-ce une approche complètement fausse pour résoudre ce problème?
Je comprends que cela peut être fait en utilisant void*
partout, mais où est le plaisir dans ce domaine?