Options de fusion en PLINQ
Quand une requête s’exécute en parallèle, PLINQ partitionne la séquence source pour que plusieurs threads puissent fonctionner simultanément sur différentes parties, généralement sur des threads distincts. Si les résultats doivent être utilisés sur un thread, par exemple, dans une boucle foreach
(For Each
en Visual Basic), les résultats de chaque thread doivent être fusionnés de nouveau en une séquence. Le type de fusion que PLINQ exécute dépend des opérateurs présents dans la requête. Par exemple, les opérateurs qui imposent un nouvel ordre des résultats doivent mettre en mémoire tampon tous les éléments de tous les threads. Du point de vue du thread utilisateur (qui est également celui de l’utilisateur de l’application), une requête entièrement mise en mémoire tampon peut s’exécuter pendant un certain temps avant qu’elle ne génère son premier résultat. D’autres opérateurs, par défaut, sont partiellement mis en mémoire tampon. Ils transmettent leurs résultats par lots. L’opérateur ForAll n’est pas mis en mémoire tampon par défaut. Il transmet immédiatement tous les éléments à partir de tous les threads.
À l’aide de la méthode WithMergeOptions, comme indiqué dans l’exemple suivant, vous pouvez fournir un indicateur à PLINQ spécifiant le type de fusion à exécuter.
var scanLines = from n in nums.AsParallel()
.WithMergeOptions(ParallelMergeOptions.NotBuffered)
where n % 2 == 0
select ExpensiveFunc(n);
Dim scanlines = From n In nums.AsParallel().WithMergeOptions(ParallelMergeOptions.NotBuffered)
Where n Mod 2 = 0
Select ExpensiveFunc(n)
Pour obtenir un exemple complet, consultez Comment : spécifier des options de fusion en PLINQ.
Si la requête ne peut pas prendre en charge l’option demandée, cette dernière sera simplement ignorée. Dans la plupart des cas, il n’est pas nécessaire de spécifier une option de fusion pour une requête PLINQ. Toutefois, dans certains cas, après avoir effectué des tests et des mesures, vous pouvez trouver qu’une requête s’exécute mieux dans un mode non défini par défaut. Cette option est souvent utilisée pour forcer un opérateur de fusion de blocs à diffuser ses résultats en continu afin de fournir une interface utilisateur plus réactive.
ParallelMergeOptions
L’énumération ParallelMergeOptions inclut les options suivantes qui spécifient, pour les formes de requête prises en charge, la manière dont la sortie finale de la requête est transmise quand les résultats sont utilisés sur un thread :
Not Buffered
Avec l’option NotBuffered, chaque élément traité est retourné à partir de chaque thread dès qu’il est généré. Ce comportement revient à « diffuser en continu » la sortie. Si l’opérateur AsOrdered est présent dans la requête,
NotBuffered
conserve l’ordre des éléments sources. Bien queNotBuffered
commence à transmettre les résultats dès qu’ils sont disponibles, la durée totale nécessaire pour générer tous les résultats peut toujours être supérieure à celles des autres options de fusion.Auto Buffered
Avec l’option AutoBuffered, la requête regroupe des éléments dans une mémoire tampon, puis transmet régulièrement tout le contenu de cette mémoire simultanément au thread utilisateur. Cette option revient à transmettre les données sources dans des « blocs » au lieu d’utiliser le comportement de « diffusion en continu » de
NotBuffered
.AutoBuffered
peut nécessiter plus de temps queNotBuffered
pour rendre le premier élément disponible sur le thread utilisateur. La taille de la mémoire tampon et le comportement exact de transmission ne sont pas configurables et peuvent varier en fonction de différents facteurs liés à la requête.FullyBuffered
Avec l’option FullyBuffered, la sortie de la requête entière est mise en mémoire tampon avant que l’un des éléments ne soit transmis. Cette option peut nécessiter plus de temps pour que le premier élément soit disponible sur le thread utilisateur, mais les résultats complets peuvent toujours être générés plus rapidement qu’avec les autres options.
Opérateurs de requête prenant en charge les options de fusion
Le tableau suivant répertorie les opérateurs qui prennent en charge tous les modes d’options de fusion, qui sont soumis aux restrictions spécifiées.
Opérateur | Restrictions |
---|---|
AsEnumerable | None |
Cast | None |
Concat | Requêtes non ordonnées qui ont uniquement une source de type Tableau ou Liste. |
DefaultIfEmpty | None |
OfType | None |
Reverse | Requêtes non ordonnées qui ont uniquement une source de type Tableau ou Liste. |
Select | None |
SelectMany | None |
Skip | None |
Take | None |
Where | None |
Tous les autres opérateurs de requête PLINQ peuvent ignorer les options de fusion fournis par l’utilisateur. Certains opérateurs de requête, tels que Reverse et OrderBy, ne peuvent pas transmettre d’éléments tant qu’ils n’ont pas tous été générés et réorganisés. Par conséquent, si vous utilisez ParallelMergeOptions dans une requête qui contient également un opérateur tel que Reverse, le comportement de fusion ne sera appliqué dans la requête qu’une fois que l’opérateur aura généré ses résultats.
La capacité de certains opérateurs à gérer les options de fusion varie selon le type de la séquence source et selon que l’opérateur AsOrdered a été utilisé antérieurement dans la requête ou non. ForAll est toujours NotBuffered ; il transmet immédiatement ses éléments. OrderBy est toujours FullyBuffered ; il doit trier l’intégralité de la liste avant toute transmission.