Une instruction LINQ est-elle plus rapide qu'une boucle «foreach»?

124

J'écris un gestionnaire de rendu de maillage et j'ai pensé que ce serait une bonne idée de regrouper tous les maillages qui utilisent le même shader, puis de les rendre pendant que je suis dans cette passe de shader.

J'utilise actuellement une foreachboucle, mais je me suis demandé si l'utilisation de LINQ pouvait me permettre d'augmenter les performances?

Neil Knight
la source
1
duplication possible des performances
Daniel Earwicker
1
Veuillez envisager de définir la réponse de @ MarcGravell sur celle acceptée, il y a des situations, linq à sql par exemple, où linq est plus rapide que for / foreach.
paqogomez

Réponses:

222

Pourquoi LINQ devrait-il être plus rapide? Il utilise également des boucles en interne.

La plupart du temps, LINQ sera un peu plus lent car il introduit une surcharge. N'utilisez pas LINQ si vous vous souciez beaucoup des performances. Utilisez LINQ car vous voulez un code plus court, mieux lisible et maintenable.

codymanix
la source
7
Donc, selon votre expérience, LINQ est plus rapide et rend le code plus difficile à lire et à maintenir? S'il vous plaît, expliquez.
codymanix
87
Je pense que vous l'avez eu à l'envers. Il dit que LINQ est PLUS LENT. Cela est dû à la tête. Il dit également que LINQ est plus facile à lire et à maintenir.
Joseph McIntyre
5
Désolé. En attendant, nous avons eu beaucoup de choses où nous avons comparé les performances de linq et de for ou foreach, et la plupart du temps, linq était plus rapide.
Offler
34
Pour être honnête, à mon avis, une boucle foreach est plus lisible que sa méthode LINQ. J'utilise LINQ parce que c'est cool :)
LuckyLikey
4
Oui, mais dans certains cas, LINQ peut vraiment améliorer la lisibilité, alors oubliez mon commentaire insensé <3
LuckyLikey
59

LINQ-to-Objects va généralement ajouter des frais généraux marginaux (itérateurs multiples, etc.). Il doit encore faire les boucles, et a délégué invoque, et devra généralement faire un peu plus pour obtenir le déréférencement à des variables capturées , etc. Dans la plupart des codes ce sera pratiquement indétectable, et plus offert par le plus simple à comprendre le code.

Avec d'autres fournisseurs LINQ comme LINQ-to-SQL, puisque la requête peut filtrer sur le serveur, elle devrait être bien meilleure qu'un flat foreach, mais vous n'auriez probablement pas fait une couverture de "select * from foo" toute façon , donc ce n'est pas nécessairement juste Comparaison.

Re PLINQ; le parallélisme peut réduire le temps écoulé , mais le temps CPU total augmentera généralement un peu en raison des frais généraux de la gestion des threads, etc.

Marc Gravell
la source
Dans une autre réponse, vous avez fait allusion à ne pas utiliser LINQ sur les collections en mémoire - par exemple List<Foo>; à la place, je devrais utiliser un foreachbloc sur ces collections. La recommandation à utiliser foreachdans ces contextes a du sens. Ma préoccupation: dois-je remplacer les requêtes LINQ par uniquement foreach si je détecte un problème de performances? À l'avenir, je considérerai le foreachpremier.
IAbstract
19

Je pense que LINQ est préférable d'utiliser sur une foreachboucle, car il vous donne un code beaucoup plus propre et facile à comprendre. Mais LINQ est plus lent que foreach. Pour en savoir plus, consultez l'article LINQ vs FOREACH vs FOR Loop Performance .

Pranay Rana
la source
15

LINQ est plus lent maintenant, mais il pourrait devenir plus rapide à un moment donné. L'avantage de LINQ est que vous n'avez pas à vous soucier de son fonctionnement. Si une nouvelle méthode est imaginée et incroyablement rapide, les gens de Microsoft peuvent l'implémenter sans même vous le dire et votre code serait beaucoup plus rapide.

Plus important encore, LINQ est simplement beaucoup plus facile à lire. Cela devrait être une raison suffisante.

Jouke van der Maas
la source
3
J'aime la ligne "Microsoft peut l'implémenter" est-ce possible, je veux dire est-ce possible sans mettre à jour le framework?
Shrivallabh
1
LINQ ne sera jamais vraiment plus rapide que l'implémentation native, car en fin de compte, cela se traduit par l'implémentation native. Il n'y a pas d'instructions CPU LINQ spéciales et de registres LINQ qui peuvent être utilisés pour traduire plus rapidement le code machine LINQ - et s'il y en avait, ils seraient également utilisés par du code non LINQ.
mg30rg
Non vrai, à un moment donné, certaines opérations de liaison peuvent devenir multithreads ou même utiliser le GPU à un moment donné.
John Stock
9

Vous pouvez obtenir une amélioration des performances si vous utilisez LINQ parallèle pour plusieurs cœurs. Voir Parallel LINQ (PLINQ) (MSDN).

mcintyre321
la source
5

J'étais intéressé par cette question, alors j'ai fait un test tout à l'heure. Utilisation de .NET Framework 4.5.2 sur un processeur Intel (R) Core (TM) i3-2328M à 2,20 GHz, 2200 Mhz, 2 cœurs avec 8 Go de RAM exécutant Microsoft Windows 7 Ultimate.

Il semble que LINQ soit plus rapide que pour chaque boucle. Voici les résultats que j'ai obtenus:

Exists = True
Time   = 174
Exists = True
Time   = 149

Il serait intéressant que certains d'entre vous puissent copier et coller ce code dans une application console et le tester également. Avant de tester avec un objet (Employee), j'ai essayé le même test avec des entiers. LINQ y était également plus rapide.

public class Program
{
    public class Employee
    {
        public int id;
        public string name;
        public string lastname;
        public DateTime dateOfBirth;

        public Employee(int id,string name,string lastname,DateTime dateOfBirth)
        {
            this.id = id;
            this.name = name;
            this.lastname = lastname;
            this.dateOfBirth = dateOfBirth;

        }
    }

    public static void Main() => StartObjTest();

    #region object test

    public static void StartObjTest()
    {
        List<Employee> items = new List<Employee>();

        for (int i = 0; i < 10000000; i++)
        {
            items.Add(new Employee(i,"name" + i,"lastname" + i,DateTime.Today));
        }

        Test3(items, items.Count-100);
        Test4(items, items.Count - 100);

        Console.Read();
    }


    public static void Test3(List<Employee> items, int idToCheck)
    {

        Stopwatch s = new Stopwatch();
        s.Start();

        bool exists = false;
        foreach (var item in items)
        {
            if (item.id == idToCheck)
            {
                exists = true;
                break;
            }
        }

        Console.WriteLine("Exists=" + exists);
        Console.WriteLine("Time=" + s.ElapsedMilliseconds);

    }

    public static void Test4(List<Employee> items, int idToCheck)
    {

        Stopwatch s = new Stopwatch();
        s.Start();

        bool exists = items.Exists(e => e.id == idToCheck);

        Console.WriteLine("Exists=" + exists);
        Console.WriteLine("Time=" + s.ElapsedMilliseconds);

    }

    #endregion


    #region int test
    public static void StartIntTest()
    {
        List<int> items = new List<int>();

        for (int i = 0; i < 10000000; i++)
        {
            items.Add(i);
        }

        Test1(items, -100);
        Test2(items, -100);

        Console.Read();
    }

    public static void Test1(List<int> items,int itemToCheck)
    {

        Stopwatch s = new Stopwatch();
        s.Start();

        bool exists = false;
        foreach (var item in items)
        {
            if (item == itemToCheck)
            {
                exists = true;
                break;
            }
        }

        Console.WriteLine("Exists=" + exists);
        Console.WriteLine("Time=" + s.ElapsedMilliseconds);

    }

    public static void Test2(List<int> items, int itemToCheck)
    {

        Stopwatch s = new Stopwatch();
        s.Start();

        bool exists = items.Contains(itemToCheck);

        Console.WriteLine("Exists=" + exists);
        Console.WriteLine("Time=" + s.ElapsedMilliseconds);

    }

    #endregion

}
Theo Kand.
la source
Voici ce que j'ai obtenu: Exists = True Time = 274 Exists = True Time = 314
PmanAce
2
avez-vous envisagé de faire le linq d'abord et pourchaque plus tard, cela pourrait aussi faire une différence
Muhammad Mamoor Khan
3
Intéressant. Je l'ai Exists=True Time=184 Exists=True Time=135sur un ordinateur portable Apache Gaming (Win 10, C # 7.3). Compilé et exécuté en mode débogage. Si j'inverse les tests, j'obtiens Exists=True Time=158 Exists=True Time=194. Il semble que Linq soit plus optimisé, je suppose.
James Wilkins
1
Il y a un malentendu dans ce post concernant le test d'objet. Bien qu'il soit vraiment intéressant de noter que List.Exists et .Contains semblent mieux performer que foreach. Il est important de noter que .Exists n'est pas une méthode linq aux entités et ne fonctionnera que sur les listes, sa méthode équivalente linq, .Any (), fonctionne certainement plus lentement que foreach.
AbdulG
3

C'est en fait une question assez complexe. Linq rend certaines choses très faciles à faire, que si vous les implémentez vous-même, vous pourriez trébucher (par exemple, linq .Except ()). Ceci s'applique en particulier à PLinq, et en particulier à l'agrégation parallèle implémentée par PLinq.

En général, pour un code identique, linq sera plus lent, en raison de la surcharge de l'appel de délégué.

Si, cependant, vous traitez un large éventail de données et appliquez des calculs relativement simples aux éléments, vous obtiendrez une augmentation considérable des performances si:

  1. Vous utilisez un tableau pour stocker les données.
  2. Vous utilisez une boucle for pour accéder à chaque élément (par opposition à foreach ou linq).

    • Remarque: lors de l'analyse comparative, rappelez-vous - si vous utilisez le même tableau / liste pour deux tests consécutifs, le cache du processeur accélérera le second. *
Adam Brown
la source