Très mauvaise performance de la tâche asynchrone exécutée sur un pool de threads en natif .Net

J’ai observé une différence étrange entre le code natif géré et .Net. J’ai un travail lourd redirigé vers threadpool. Lorsque vous exécutez l’application en code managé, tout se passe bien, mais dès que j’active la compilation native, la tâche est exécutée plusieurs fois plus lentement et si lentement qu’elle bloque le thread d’interface utilisateur (je suppose que le processeur est tellement surchargé).

Voici deux captures d’écran de la sortie de débogage, celle de gauche du code managé et celle de droite de la compilation native. Comme vous pouvez le constater, le temps consommé par la tâche d’interface utilisateur est pratiquement le même dans les deux cas, jusqu’au moment où le travail de threadpool est lancé – puis, dans la version gérée, le temps écoulé de l’interface utilisateur augmente (en fait, l’interface utilisateur est bloquée et vous ne pouvez rien faire). Les horaires de travail threadpool parlent d’eux-mêmes.

Géré Originaire de

Le code exemple pour reproduire le problème:

private int max = 2000; private async void UIJob_Click(object sender, RoutedEventArgs e) { IProgress progress = new Progress((p) => { MyProgressBar.Value = (double)p / max; }); await Task.Run(async () => { await SomeUIJob(progress); }); } private async Task SomeUIJob(IProgress progress) { Stopwatch watch = new Stopwatch(); watch.Start(); for (int i = 0; i  {watch.ElapsedMilliseconds}"); watch.Restart(); } await Task.Delay(1); progress.Report(i); } } private async void ThreadpoolJob_Click(object sender, RoutedEventArgs e) { Debug.WriteLine("Firing on Threadpool"); await Task.Run(() => { double a = 0.314; Stopwatch watch = new Stopwatch(); watch.Start(); for (int i = 0; i  a value = {a} got in {watch.ElapsedMilliseconds} ms"); watch.Restart(); }; } }); Debug.WriteLine("Finished with Threadpool"); } 

Si vous avez besoin d’un échantillon complet, vous pouvez le télécharger ici .

Comme j’ai testé, la différence apparaît à la fois sur le code optimisé / non optimisé, dans les versions de débogage et de publication.

Quelqu’un a-t-il une idée de ce qui peut causer le problème?

    Ce problème est dû au fait que la boucle mathématique «ThreadPool» est à l’origine de la famine du GC. Pour l’essentiel, le GC a décidé qu’il devait s’exécuter (car il souhaitait effectuer une allocation d’interopérabilité) et il essayait d’arrêter tous les threads nécessaires à la collecte / compactage. Malheureusement, nous n’avons pas ajouté la possibilité pour .NET Native de détourner des boucles dynamics comme celle que vous avez ci-dessous. Ceci est brièvement mentionné sur la page Migration de votre Windows Store App vers .NET Native en tant que:

    Une boucle infinie sans passer d’appel (par exemple, while (true);) sur n’importe quel thread peut entraîner l’arrêt de l’application. De même, des attentes longues ou infinies peuvent entraîner l’arrêt de l’application.

    Une façon de contourner ce problème consiste à append un site d’appel dans votre boucle (le GC est très heureux d’interrompre votre fil lorsqu’il essaie d’appeler une autre méthode!).

      for (long i = 0; i < 5000000000; i++) { MaybeGCMeHere(); // new callsite a = Math.Sqrt(a) + Math.Sqrt(a + 1) + i; if (i % 1000000000 == 0) { Debug.WriteLine($"Threadpool -> a value = {a} got in {watch.ElapsedMilliseconds} ms"); watch.Restart(); }; } ... [MethodImpl(MethodImplOptions.NoInlining)] // need this so the callsite isn't optimized away private void MaybeGCMeHere() { } 

    L’inconvénient est que vous aurez cette “moche” à la recherche et vous pouvez souffrir un peu des instructions ajoutées. J’ai fait savoir à certaines personnes ici que cette chose que nous avons supposée être “extrêmement rare” est en fait frappée par un client et nous verrons ce qui peut être fait pour y remédier.

    Merci pour le rapport!

    Mise à jour: Nous avons apporté de grandes améliorations à ce scénario et serons en mesure de détourner les threads les plus longs pour GC. Ces correctifs seront disponibles dans le jeu d’outils UWP de la mise à jour 2 probablement en avril? (Je ne contrôle pas le calendrier d’expédition :-))

    Mise à jour de la mise à jour: les nouveaux outils sont maintenant disponibles dans les outils UWP 1.3.1. Nous ne nous attendons pas à une solution parfaite pour les threads qui luttent agressivement contre le piratage par le GC, mais je m’attends à ce que ce scénario soit bien meilleur avec les derniers outils. Laissez nous savoir!